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

575 lines
17 KiB

  1. /*
  2. * Copyright (C) 2023 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 "color/color.hpp"
  21. #include "config.hpp"
  22. #include "debug/log.hpp"
  23. #include "entity/archetype.hpp"
  24. #include "entity/commands.hpp"
  25. #include "game/component/atmosphere.hpp"
  26. #include "game/component/blackbody.hpp"
  27. #include "game/component/celestial-body.hpp"
  28. #include "game/component/observer.hpp"
  29. #include "game/component/orbit.hpp"
  30. #include "game/component/terrain.hpp"
  31. #include "game/component/transform.hpp"
  32. #include "game/system/astronomy.hpp"
  33. #include "game/system/atmosphere.hpp"
  34. #include "game/system/orbit.hpp"
  35. #include "game/system/terrain.hpp"
  36. #include "game/world.hpp"
  37. #include "geom/solid-angle.hpp"
  38. #include "geom/spherical.hpp"
  39. #include "gl/drawing-mode.hpp"
  40. #include "gl/texture-filter.hpp"
  41. #include "gl/texture-wrapping.hpp"
  42. #include "gl/vertex-array.hpp"
  43. #include "gl/vertex-attribute.hpp"
  44. #include "gl/vertex-buffer.hpp"
  45. #include "math/hash/hash.hpp"
  46. #include "math/noise/noise.hpp"
  47. #include "physics/light/photometry.hpp"
  48. #include "physics/light/vmag.hpp"
  49. #include "physics/orbit/ephemeris.hpp"
  50. #include "physics/orbit/orbit.hpp"
  51. #include "physics/time/constants.hpp"
  52. #include "physics/time/gregorian.hpp"
  53. #include "physics/time/utc.hpp"
  54. #include "render/material-flags.hpp"
  55. #include "render/material.hpp"
  56. #include "render/model.hpp"
  57. #include "render/passes/ground-pass.hpp"
  58. #include "render/passes/shadow-map-pass.hpp"
  59. #include "render/passes/sky-pass.hpp"
  60. #include "render/vertex-attribute.hpp"
  61. #include "resources/image.hpp"
  62. #include "resources/json.hpp"
  63. #include "resources/resource-manager.hpp"
  64. #include "scene/ambient-light.hpp"
  65. #include "scene/directional-light.hpp"
  66. #include "scene/text.hpp"
  67. #include "i18n/string-table.hpp"
  68. #include <algorithm>
  69. #include <execution>
  70. #include <fstream>
  71. #include <stb/stb_image_write.h>
  72. namespace game {
  73. namespace world {
  74. /// Loads an ephemeris.
  75. static void load_ephemeris(game::context& ctx);
  76. /// Creates the fixed stars.
  77. static void create_stars(game::context& ctx);
  78. /// Creates the Sun.
  79. static void create_sun(game::context& ctx);
  80. /// Creates the Earth-Moon system.
  81. static void create_earth_moon_system(game::context& ctx);
  82. /// Creates the Earth.
  83. static void create_earth(game::context& ctx);
  84. /// Creates the Moon.
  85. static void create_moon(game::context& ctx);
  86. void cosmogenesis(game::context& ctx)
  87. {
  88. debug::log::trace("Generating cosmos...");
  89. load_ephemeris(ctx);
  90. create_stars(ctx);
  91. create_sun(ctx);
  92. create_earth_moon_system(ctx);
  93. debug::log::trace("Generated cosmos");
  94. }
  95. void create_observer(game::context& ctx)
  96. {
  97. debug::log::trace("Creating observer...");
  98. {
  99. // Create observer entity
  100. entity::id observer_eid = ctx.entity_registry->create();
  101. ctx.entities["observer"] = observer_eid;
  102. // Construct observer component
  103. game::component::observer observer;
  104. // Set observer reference body
  105. if (auto it = ctx.entities.find("earth"); it != ctx.entities.end())
  106. observer.reference_body_eid = it->second;
  107. else
  108. observer.reference_body_eid = entt::null;
  109. // Set observer location
  110. observer.elevation = 0.0;
  111. observer.latitude = 0.0;
  112. observer.longitude = 0.0;
  113. // Assign observer component to observer entity
  114. ctx.entity_registry->emplace<game::component::observer>(observer_eid, observer);
  115. // Set atmosphere system active atmosphere
  116. ctx.atmosphere_system->set_active_atmosphere(observer.reference_body_eid);
  117. // Set astronomy system observer
  118. ctx.astronomy_system->set_observer(observer_eid);
  119. }
  120. debug::log::trace("Created observer");
  121. }
  122. void set_location(game::context& ctx, double elevation, double latitude, double longitude)
  123. {
  124. if (auto it = ctx.entities.find("observer"); it != ctx.entities.end())
  125. {
  126. entity::id observer_eid = it->second;
  127. if (ctx.entity_registry->valid(observer_eid) && ctx.entity_registry->all_of<game::component::observer>(observer_eid))
  128. {
  129. // Update observer location
  130. ctx.entity_registry->patch<game::component::observer>
  131. (
  132. observer_eid,
  133. [&](auto& component)
  134. {
  135. component.elevation = elevation;
  136. component.latitude = latitude;
  137. component.longitude = longitude;
  138. }
  139. );
  140. }
  141. }
  142. }
  143. void set_time(game::context& ctx, double t)
  144. {
  145. try
  146. {
  147. ctx.astronomy_system->set_time(t);
  148. ctx.orbit_system->set_time(t);
  149. debug::log::info("Set time to UT1 {}", t);
  150. }
  151. catch (const std::exception& e)
  152. {
  153. debug::log::error("Failed to set time to UT1 {}: {}", t, e.what());
  154. }
  155. }
  156. void set_time(game::context& ctx, int year, int month, int day, int hour, int minute, double second)
  157. {
  158. double longitude = 0.0;
  159. // Get longitude of observer
  160. if (auto it = ctx.entities.find("observer"); it != ctx.entities.end())
  161. {
  162. entity::id observer_eid = it->second;
  163. if (ctx.entity_registry->valid(observer_eid))
  164. {
  165. const auto observer = ctx.entity_registry->try_get<game::component::observer>(observer_eid);
  166. if (observer)
  167. longitude = observer->longitude;
  168. }
  169. }
  170. // Calculate UTC offset at longitude
  171. const double utc_offset = physics::time::utc::offset<double>(longitude);
  172. // Convert time from Gregorian to UT1
  173. const double t = physics::time::gregorian::to_ut1<double>(year, month, day, hour, minute, second, utc_offset);
  174. set_time(ctx, t);
  175. }
  176. void set_time_scale(game::context& ctx, double scale)
  177. {
  178. // Convert time scale from seconds to days
  179. const double astronomical_scale = scale / physics::time::seconds_per_day<double>;
  180. ctx.orbit_system->set_time_scale(astronomical_scale);
  181. ctx.astronomy_system->set_time_scale(astronomical_scale);
  182. }
  183. void load_ephemeris(game::context& ctx)
  184. {
  185. ctx.orbit_system->set_ephemeris(ctx.resource_manager->load<physics::orbit::ephemeris<double>>("de421.eph"));
  186. }
  187. void create_stars(game::context& ctx)
  188. {
  189. debug::log::trace("Generating fixed stars...");
  190. // Load star catalog
  191. i18n::string_table* star_catalog = nullptr;
  192. star_catalog = ctx.resource_manager->load<i18n::string_table>("hipparcos-7.tsv");
  193. // Allocate star catalog vertex data
  194. std::size_t star_count = 0;
  195. if (star_catalog->size() > 0)
  196. star_count = star_catalog->size() - 1;
  197. std::size_t star_vertex_size = 7;
  198. std::size_t star_vertex_stride = star_vertex_size * sizeof(float);
  199. float* star_vertex_data = new float[star_count * star_vertex_size];
  200. float* star_vertex = star_vertex_data;
  201. // Init starlight illuminance
  202. double3 starlight_illuminance = {0, 0, 0};
  203. // Build star catalog vertex data
  204. for (std::size_t i = 1; i < star_catalog->size(); ++i)
  205. {
  206. const i18n::string_table_row& catalog_row = (*star_catalog)[i];
  207. // Parse star catalog item
  208. float ra = 0.0;
  209. float dec = 0.0;
  210. float vmag = 0.0;
  211. float bv = 0.0;
  212. try
  213. {
  214. ra = std::stof(catalog_row[1]);
  215. dec = std::stof(catalog_row[2]);
  216. vmag = std::stof(catalog_row[3]);
  217. bv = std::stof(catalog_row[4]);
  218. }
  219. catch (const std::exception&)
  220. {
  221. debug::log::warning("Invalid star catalog item on row {}", i);
  222. continue;
  223. }
  224. // Convert right ascension and declination from degrees to radians
  225. ra = math::wrap_radians(math::radians(ra));
  226. dec = math::wrap_radians(math::radians(dec));
  227. // Convert ICRF coordinates from spherical to Cartesian
  228. float3 position = physics::orbit::frame::bci::cartesian(float3{1.0f, dec, ra});
  229. // Convert color index to color temperature
  230. float cct = color::index::bv_to_cct(bv);
  231. // Calculate XYZ color from color temperature
  232. float3 color_xyz = color::cct::to_xyz(cct);
  233. // Transform XYZ color to ACEScg colorspace
  234. float3 color_acescg = color::aces::ap1<float>.from_xyz * color_xyz;
  235. // Convert apparent magnitude to brightness factor relative to a 0th magnitude star
  236. float brightness = physics::light::vmag::to_brightness(vmag);
  237. // Build vertex
  238. *(star_vertex++) = position.x();
  239. *(star_vertex++) = position.y();
  240. *(star_vertex++) = position.z();
  241. *(star_vertex++) = color_acescg.x();
  242. *(star_vertex++) = color_acescg.y();
  243. *(star_vertex++) = color_acescg.z();
  244. *(star_vertex++) = brightness;
  245. // Calculate spectral illuminance
  246. double3 illuminance = double3(color_acescg * physics::light::vmag::to_illuminance(vmag));
  247. // Add spectral illuminance to total starlight illuminance
  248. starlight_illuminance += illuminance;
  249. }
  250. // Unload star catalog
  251. ctx.resource_manager->unload("hipparcos-7.tsv");
  252. // Allocate stars model
  253. render::model* stars_model = new render::model();
  254. // Get model VBO and VAO
  255. gl::vertex_buffer* vbo = stars_model->get_vertex_buffer();
  256. gl::vertex_array* vao = stars_model->get_vertex_array();
  257. // Resize model VBO and upload vertex data
  258. vbo->resize(star_count * star_vertex_stride, star_vertex_data);
  259. // Free star catalog vertex data
  260. delete[] star_vertex_data;
  261. std::size_t attribute_offset = 0;
  262. // Define position vertex attribute
  263. gl::vertex_attribute position_attribute;
  264. position_attribute.buffer = vbo;
  265. position_attribute.offset = attribute_offset;
  266. position_attribute.stride = star_vertex_stride;
  267. position_attribute.type = gl::vertex_attribute_type::float_32;
  268. position_attribute.components = 3;
  269. attribute_offset += position_attribute.components * sizeof(float);
  270. // Define color vertex attribute
  271. gl::vertex_attribute color_attribute;
  272. color_attribute.buffer = vbo;
  273. color_attribute.offset = attribute_offset;
  274. color_attribute.stride = star_vertex_stride;
  275. color_attribute.type = gl::vertex_attribute_type::float_32;
  276. color_attribute.components = 4;
  277. attribute_offset += color_attribute.components * sizeof(float);
  278. // Bind vertex attributes to VAO
  279. vao->bind(render::vertex_attribute::position, position_attribute);
  280. vao->bind(render::vertex_attribute::color, color_attribute);
  281. // Load star material
  282. render::material* star_material = ctx.resource_manager->load<render::material>("fixed-star.mtl");
  283. // Create model group
  284. render::model_group* stars_model_group = stars_model->add_group("stars");
  285. stars_model_group->set_material(star_material);
  286. stars_model_group->set_drawing_mode(gl::drawing_mode::points);
  287. stars_model_group->set_start_index(0);
  288. stars_model_group->set_index_count(star_count);
  289. // Pass stars model to sky pass
  290. ctx.sky_pass->set_stars_model(stars_model);
  291. // Pass starlight illuminance to astronomy system
  292. ctx.astronomy_system->set_starlight_illuminance(starlight_illuminance);
  293. debug::log::trace("Generated fixed stars");
  294. }
  295. void create_sun(game::context& ctx)
  296. {
  297. debug::log::trace("Generating Sun...");
  298. {
  299. // Create sun entity
  300. entity::archetype* sun_archetype = ctx.resource_manager->load<entity::archetype>("sun.ent");
  301. entity::id sun_eid = sun_archetype->create(*ctx.entity_registry);
  302. ctx.entities["sun"] = sun_eid;
  303. // Create sun directional light scene object
  304. scene::directional_light* sun_light = new scene::directional_light();
  305. sun_light->set_color({0, 0, 0});
  306. sun_light->set_shadow_caster(true);
  307. sun_light->set_shadow_framebuffer(ctx.shadow_map_framebuffer);
  308. sun_light->set_shadow_bias(0.005f);
  309. sun_light->set_shadow_cascade_count(4);
  310. sun_light->set_shadow_cascade_coverage(0.15f);
  311. sun_light->set_shadow_cascade_distribution(0.8f);
  312. sun_light->update_tweens();
  313. // Create sky ambient light scene object
  314. scene::ambient_light* sky_light = new scene::ambient_light();
  315. sky_light->set_color({0, 0, 0});
  316. sky_light->update_tweens();
  317. // Create bounce directional light scene object
  318. scene::directional_light* bounce_light = new scene::directional_light();
  319. bounce_light->set_color({0, 0, 0});
  320. bounce_light->look_at({0, 0, 0}, {0, 1, 0}, {1, 0, 0});
  321. bounce_light->update_tweens();
  322. // Add sun light scene objects to surface scene
  323. ctx.surface_scene->add_object(sun_light);
  324. ctx.surface_scene->add_object(sky_light);
  325. //ctx.surface_scene->add_object(bounce_light);
  326. // Pass direct sun light scene object to shadow map pass and astronomy system
  327. ctx.astronomy_system->set_sun_light(sun_light);
  328. ctx.astronomy_system->set_sky_light(sky_light);
  329. ctx.astronomy_system->set_bounce_light(bounce_light);
  330. }
  331. debug::log::trace("Generated Sun");
  332. }
  333. void create_earth_moon_system(game::context& ctx)
  334. {
  335. debug::log::trace("Generating Earth-Moon system...");
  336. {
  337. // Create Earth-Moon barycenter entity
  338. entity::archetype* em_bary_archetype = ctx.resource_manager->load<entity::archetype>("em-bary.ent");
  339. entity::id em_bary_eid = em_bary_archetype->create(*ctx.entity_registry);
  340. ctx.entities["em_bary"] = em_bary_eid;
  341. // Create Earth
  342. create_earth(ctx);
  343. // Create Moon
  344. create_moon(ctx);
  345. }
  346. debug::log::trace("Generated Earth-Moon system");
  347. }
  348. void create_earth(game::context& ctx)
  349. {
  350. debug::log::trace("Generating Earth...");
  351. {
  352. // Create earth entity
  353. entity::archetype* earth_archetype = ctx.resource_manager->load<entity::archetype>("earth.ent");
  354. entity::id earth_eid = earth_archetype->create(*ctx.entity_registry);
  355. ctx.entities["earth"] = earth_eid;
  356. // Assign orbital parent
  357. ctx.entity_registry->get<game::component::orbit>(earth_eid).parent = ctx.entities["em_bary"];
  358. }
  359. debug::log::trace("Generated Earth");
  360. }
  361. void create_moon(game::context& ctx)
  362. {
  363. debug::log::trace("Generating Moon...");
  364. {
  365. // Create lunar entity
  366. entity::archetype* moon_archetype = ctx.resource_manager->load<entity::archetype>("moon.ent");
  367. entity::id moon_eid = moon_archetype->create(*ctx.entity_registry);
  368. ctx.entities["moon"] = moon_eid;
  369. // Assign orbital parent
  370. ctx.entity_registry->get<game::component::orbit>(moon_eid).parent = ctx.entities["em_bary"];
  371. // Pass moon model to sky pass
  372. ctx.sky_pass->set_moon_model(ctx.resource_manager->load<render::model>("moon.mdl"));
  373. // Create moon directional light scene object
  374. scene::directional_light* moon_light = new scene::directional_light();
  375. moon_light->set_color({0, 0, 0});
  376. moon_light->update_tweens();
  377. // Add moon light scene objects to surface scene
  378. ctx.surface_scene->add_object(moon_light);
  379. // Pass moon light scene object to astronomy system
  380. ctx.astronomy_system->set_moon_light(moon_light);
  381. }
  382. debug::log::trace("Generated Moon");
  383. }
  384. void enter_ecoregion(game::context& ctx, const ecoregion& ecoregion)
  385. {
  386. /*
  387. image img;
  388. img.format(1, 4);
  389. img.resize(2048, 2048);
  390. auto width = img.get_width();
  391. auto height = img.get_height();
  392. unsigned char* pixels = (unsigned char*)img.data();
  393. const float frequency = 400.0f;
  394. float scale_x = 1.0f / static_cast<float>(width - 1) * frequency;
  395. float scale_y = 1.0f / static_cast<float>(height - 1) * frequency;
  396. std::for_each
  397. (
  398. std::execution::par_unseq,
  399. img.begin<math::vector<unsigned char, 4>>(),
  400. img.end<math::vector<unsigned char, 4>>(),
  401. [pixels, width, height, scale_x, scale_y, frequency](auto& pixel)
  402. {
  403. const std::size_t i = &pixel - (math::vector<unsigned char, 4>*)pixels;
  404. const std::size_t y = i / width;
  405. const std::size_t x = i % width;
  406. const float2 position =
  407. {
  408. static_cast<float>(x) * scale_x,
  409. static_cast<float>(y) * scale_y
  410. };
  411. const auto
  412. [
  413. f1_sqr_distance,
  414. f1_displacement,
  415. f1_id
  416. ] = math::noise::voronoi::f1<float, 2>(position, 1.0f, {frequency, frequency});
  417. const float f1_distance = std::sqrt(f1_sqr_distance);
  418. const float2 uv = (position + f1_displacement) / frequency;
  419. pixel =
  420. {
  421. static_cast<unsigned char>(std::min(255.0f, f1_distance * 255.0f)),
  422. static_cast<unsigned char>(std::min(255.0f, uv[0] * 255.0f)),
  423. static_cast<unsigned char>(std::min(255.0f, uv[1] * 255.0f)),
  424. static_cast<unsigned char>(f1_id % 256)
  425. };
  426. }
  427. );
  428. stbi_flip_vertically_on_write(1);
  429. stbi_write_tga((ctx.screenshots_path / "voronoi-f1-400-nc8-2k.tga").string().c_str(), img.get_width(), img.get_height(), img.get_channel_count(), img.data());
  430. */
  431. debug::log::trace("Entering ecoregion {}...", ecoregion.name);
  432. {
  433. // Set active ecoregion
  434. ctx.active_ecoregion = &ecoregion;
  435. // Set location
  436. game::world::set_location(ctx, ecoregion.elevation, ecoregion.latitude, ecoregion.longitude);
  437. // Setup sky
  438. ctx.sky_pass->set_sky_model(ctx.resource_manager->load<render::model>("celestial-hemisphere.mdl"));
  439. render::model* terrestrial_hemisphere_model = ctx.resource_manager->load<render::model>("terrestrial-hemisphere.mdl");
  440. (*terrestrial_hemisphere_model->get_groups())[0]->set_material(ecoregion.horizon_material);
  441. ctx.ground_pass->set_ground_model(terrestrial_hemisphere_model);
  442. // Setup terrain
  443. ctx.terrain_system->set_patch_material(ecoregion.terrain_material);
  444. ctx.terrain_system->set_elevation_function
  445. (
  446. [](float x, float z) -> float
  447. {
  448. const float2 position = float2{x, z};
  449. const std::size_t octaves = 3;
  450. const float lacunarity = 1.5f;
  451. const float gain = 0.5f;
  452. const float fbm = math::noise::fbm
  453. (
  454. position * 0.005f,
  455. octaves,
  456. lacunarity,
  457. gain
  458. );
  459. float y = fbm * 4.0f;
  460. return y;
  461. }
  462. );
  463. ctx.astronomy_system->set_bounce_albedo(double3(ecoregion.terrain_albedo));
  464. }
  465. debug::log::trace("Entered ecoregion {}", ecoregion.name);
  466. }
  467. } // namespace world
  468. } // namespace game