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

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