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

627 lines
24 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 "game/system/astronomy.hpp"
  20. #include "astro/apparent-size.hpp"
  21. #include "game/component/blackbody.hpp"
  22. #include "game/component/transform.hpp"
  23. #include "game/component/diffuse-reflector.hpp"
  24. #include "geom/intersection.hpp"
  25. #include "geom/cartesian.hpp"
  26. #include "color/color.hpp"
  27. #include "physics/orbit/orbit.hpp"
  28. #include "physics/time/ut1.hpp"
  29. #include "physics/time/jd.hpp"
  30. #include "physics/light/photometry.hpp"
  31. #include "physics/light/luminosity.hpp"
  32. #include "physics/light/refraction.hpp"
  33. #include "physics/gas/atmosphere.hpp"
  34. #include "geom/cartesian.hpp"
  35. #include "astro/apparent-size.hpp"
  36. #include "geom/solid-angle.hpp"
  37. #include "math/polynomial.hpp"
  38. namespace game {
  39. namespace system {
  40. astronomy::astronomy(entity::registry& registry):
  41. updatable(registry),
  42. time_days(0.0),
  43. time_centuries(0.0),
  44. time_scale(1.0),
  45. observer_eid(entt::null),
  46. reference_body_eid(entt::null),
  47. transmittance_samples(0),
  48. sun_light(nullptr),
  49. sky_light(nullptr),
  50. moon_light(nullptr),
  51. bounce_light(nullptr),
  52. bounce_albedo{0, 0, 0},
  53. sky_pass(nullptr),
  54. starlight_illuminance{0, 0, 0}
  55. {
  56. // Construct ENU to EUS transformation
  57. enu_to_eus = math::transformation::se3<double>
  58. {
  59. {0, 0, 0},
  60. math::quaternion<double>::rotate_x(-math::half_pi<double>)
  61. };
  62. registry.on_construct<game::component::observer>().connect<&astronomy::on_observer_modified>(this);
  63. registry.on_update<game::component::observer>().connect<&astronomy::on_observer_modified>(this);
  64. registry.on_destroy<game::component::observer>().connect<&astronomy::on_observer_destroyed>(this);
  65. registry.on_construct<game::component::celestial_body>().connect<&astronomy::on_celestial_body_modified>(this);
  66. registry.on_update<game::component::celestial_body>().connect<&astronomy::on_celestial_body_modified>(this);
  67. registry.on_destroy<game::component::celestial_body>().connect<&astronomy::on_celestial_body_destroyed>(this);
  68. registry.on_construct<game::component::orbit>().connect<&astronomy::on_orbit_modified>(this);
  69. registry.on_update<game::component::orbit>().connect<&astronomy::on_orbit_modified>(this);
  70. registry.on_destroy<game::component::orbit>().connect<&astronomy::on_orbit_destroyed>(this);
  71. registry.on_construct<game::component::atmosphere>().connect<&astronomy::on_atmosphere_modified>(this);
  72. registry.on_update<game::component::atmosphere>().connect<&astronomy::on_atmosphere_modified>(this);
  73. registry.on_destroy<game::component::atmosphere>().connect<&astronomy::on_atmosphere_destroyed>(this);
  74. }
  75. astronomy::~astronomy()
  76. {
  77. registry.on_construct<game::component::observer>().disconnect<&astronomy::on_observer_modified>(this);
  78. registry.on_update<game::component::observer>().disconnect<&astronomy::on_observer_modified>(this);
  79. registry.on_destroy<game::component::observer>().disconnect<&astronomy::on_observer_destroyed>(this);
  80. registry.on_construct<game::component::celestial_body>().disconnect<&astronomy::on_celestial_body_modified>(this);
  81. registry.on_update<game::component::celestial_body>().disconnect<&astronomy::on_celestial_body_modified>(this);
  82. registry.on_destroy<game::component::celestial_body>().disconnect<&astronomy::on_celestial_body_destroyed>(this);
  83. registry.on_construct<game::component::orbit>().disconnect<&astronomy::on_orbit_modified>(this);
  84. registry.on_update<game::component::orbit>().disconnect<&astronomy::on_orbit_modified>(this);
  85. registry.on_destroy<game::component::orbit>().disconnect<&astronomy::on_orbit_destroyed>(this);
  86. registry.on_construct<game::component::atmosphere>().disconnect<&astronomy::on_atmosphere_modified>(this);
  87. registry.on_update<game::component::atmosphere>().disconnect<&astronomy::on_atmosphere_modified>(this);
  88. registry.on_destroy<game::component::atmosphere>().disconnect<&astronomy::on_atmosphere_destroyed>(this);
  89. }
  90. void astronomy::update(double t, double dt)
  91. {
  92. double3 sky_light_illuminance = {0.0, 0.0, 0.0};
  93. // Add scaled timestep to current time
  94. set_time(time_days + dt * time_scale);
  95. // Abort if no valid observer entity or reference body entity
  96. if (observer_eid == entt::null || reference_body_eid == entt::null)
  97. return;
  98. // Get pointer to observer component
  99. const auto observer = registry.try_get<component::observer>(observer_eid);
  100. // Abort if no observer component
  101. if (!observer)
  102. return;
  103. // Get pointers to reference body components
  104. const auto
  105. [
  106. reference_body,
  107. reference_orbit,
  108. reference_atmosphere
  109. ] = registry.try_get<component::celestial_body, component::orbit, component::atmosphere>(reference_body_eid);
  110. // Abort if no reference body or reference orbit
  111. if (!reference_body || !reference_orbit)
  112. return;
  113. // Update ICRF to EUS transformation
  114. update_icrf_to_eus(*reference_body, *reference_orbit);
  115. // Set the transform component translations of orbiting bodies to their topocentric positions
  116. registry.view<component::celestial_body, component::orbit, component::transform>().each(
  117. [&](entity::id entity_id, const auto& body, const auto& orbit, auto& transform)
  118. {
  119. // Skip reference body entity
  120. if (entity_id == reference_body_eid)
  121. return;
  122. // Transform orbital Cartesian position (r) from the ICRF frame to the EUS frame
  123. const double3 r_eus = icrf_to_eus * orbit.position;
  124. // Evaluate body orientation polynomials
  125. const double body_pole_ra = math::polynomial::horner(body.pole_ra.begin(), body.pole_ra.end(), time_centuries);
  126. const double body_pole_dec = math::polynomial::horner(body.pole_dec.begin(), body.pole_dec.end(), time_centuries);
  127. const double body_prime_meridian = math::polynomial::horner(body.prime_meridian.begin(), body.prime_meridian.end(), time_days);
  128. // Determine body orientation in the ICRF frame
  129. math::quaternion<double> rotation_icrf = physics::orbit::frame::bcbf::to_bci
  130. (
  131. body_pole_ra,
  132. body_pole_dec,
  133. body_prime_meridian
  134. ).r;
  135. // Transform body orientation from the ICRF frame to the EUS frame.
  136. math::quaternion<double> rotation_eus = math::normalize(icrf_to_eus.r * rotation_icrf);
  137. // Update local transform
  138. if (orbit.parent != entt::null)
  139. {
  140. transform.local.translation = math::normalize(float3(r_eus));
  141. transform.local.rotation = math::quaternion<float>(rotation_eus);
  142. transform.local.scale = {1.0f, 1.0f, 1.0f};
  143. }
  144. });
  145. constexpr double3 bounce_normal = {0, 1, 0};
  146. double3 bounce_illuminance = {0, 0, 0};
  147. // Update blackbody lighting
  148. registry.view<component::celestial_body, component::orbit, component::blackbody>().each(
  149. [&, bounce_normal](entity::id entity_id, const auto& blackbody_body, const auto& blackbody_orbit, const auto& blackbody)
  150. {
  151. // Transform blackbody position from ICRF frame to EUS frame
  152. const double3 blackbody_position_eus = icrf_to_eus * blackbody_orbit.position;
  153. // Measure distance and direction, in EUS frame, from observer to blackbody
  154. const double observer_blackbody_distance = math::length(blackbody_position_eus);
  155. const double3 observer_blackbody_direction_eus = blackbody_position_eus / observer_blackbody_distance;
  156. // Measure blackbody solid angle as seen by observer
  157. const double observer_blackbody_angular_radius = astro::angular_radius(blackbody_body.radius, observer_blackbody_distance);
  158. const double observer_blackbody_solid_angle = geom::solid_angle::cone(observer_blackbody_angular_radius);
  159. // Calculate illuminance from blackbody reaching observer
  160. const double3 observer_blackbody_illuminance = blackbody.luminance * observer_blackbody_solid_angle;
  161. // Calculate illuminance from blackbody reaching observer after atmospheric extinction
  162. double3 observer_blackbody_transmitted_illuminance = observer_blackbody_illuminance;
  163. if (reference_atmosphere)
  164. {
  165. // Construct ray at observer pointing towards the blackbody
  166. const geom::ray<double> ray = {{0, 0, 0}, observer_blackbody_direction_eus};
  167. // Integrate atmospheric spectral transmittance factor between observer and blackbody
  168. const double3 transmittance = integrate_transmittance(*observer, *reference_body, *reference_atmosphere, ray);
  169. // Attenuate illuminance from blackbody reaching observer by spectral transmittance factor
  170. observer_blackbody_transmitted_illuminance *= transmittance;
  171. }
  172. // Update sun light
  173. if (sun_light != nullptr)
  174. {
  175. const double3 blackbody_up_eus = icrf_to_eus.r * double3{0, 0, 1};
  176. sun_light->set_rotation
  177. (
  178. math::look_rotation
  179. (
  180. float3(-observer_blackbody_direction_eus),
  181. float3(blackbody_up_eus)
  182. )
  183. );
  184. sun_light->set_color(float3(observer_blackbody_transmitted_illuminance));
  185. // Bounce sun light
  186. bounce_illuminance += std::max(0.0, math::dot(bounce_normal, -observer_blackbody_direction_eus)) * observer_blackbody_transmitted_illuminance * bounce_albedo;
  187. }
  188. // Update sky light
  189. if (sky_light != nullptr)
  190. {
  191. // Calculate sky illuminance
  192. double3 blackbody_position_enu_spherical = physics::orbit::frame::enu::spherical(enu_to_eus.inverse() * blackbody_position_eus);
  193. const double sky_illuminance = 25000.0 * std::max<double>(0.0, std::sin(blackbody_position_enu_spherical.y()));
  194. // Add sky illuminance to sky light illuminance
  195. sky_light_illuminance += {sky_illuminance, sky_illuminance, sky_illuminance};
  196. // Add starlight illuminance to sky light illuminance
  197. sky_light_illuminance += starlight_illuminance;
  198. // Update sky light
  199. sky_light->set_color(float3(sky_light_illuminance));
  200. // Bounce sky light
  201. bounce_illuminance += sky_light_illuminance * bounce_albedo;
  202. }
  203. // Upload blackbody params to sky pass
  204. if (this->sky_pass)
  205. {
  206. this->sky_pass->set_sun_position(float3(blackbody_position_eus));
  207. this->sky_pass->set_sun_luminance(float3(blackbody.luminance));
  208. this->sky_pass->set_sun_illuminance(float3(observer_blackbody_illuminance), float3(observer_blackbody_transmitted_illuminance));
  209. this->sky_pass->set_sun_angular_radius(static_cast<float>(observer_blackbody_angular_radius));
  210. }
  211. // Update diffuse reflectors
  212. this->registry.view<component::celestial_body, component::orbit, component::diffuse_reflector, component::transform>().each(
  213. [&, bounce_normal](entity::id entity_id, const auto& reflector_body, const auto& reflector_orbit, const auto& reflector, const auto& transform)
  214. {
  215. // Transform reflector position from ICRF frame to EUS frame
  216. const double3 reflector_position_eus = icrf_to_eus * reflector_orbit.position;
  217. // Measure distance and direction, in EUS frame, from observer to reflector
  218. const double observer_reflector_distance = math::length(reflector_position_eus);
  219. const double3 observer_reflector_direction_eus = reflector_position_eus / observer_reflector_distance;
  220. // Measure distance and direction, in EUS frame, from reflector to blackbody
  221. double3 reflector_blackbody_direction_eus = blackbody_position_eus - reflector_position_eus;
  222. const double reflector_blackbody_distance = math::length(reflector_blackbody_direction_eus);
  223. reflector_blackbody_direction_eus /= reflector_blackbody_distance;
  224. // Measure blackbody solid angle as seen by reflector
  225. const double reflector_blackbody_angular_radius = astro::angular_radius(blackbody_body.radius, reflector_blackbody_distance);
  226. const double reflector_blackbody_solid_angle = geom::solid_angle::cone(reflector_blackbody_angular_radius);
  227. // Calculate blackbody illuminance reaching reflector
  228. const double3 reflector_blackbody_illuminance = blackbody.luminance * reflector_blackbody_solid_angle;
  229. // Measure reflector solid angle as seen by observer
  230. const double observer_reflector_angular_radius = astro::angular_radius(reflector_body.radius, observer_reflector_distance);
  231. const double observer_reflector_solid_angle = geom::solid_angle::cone(observer_reflector_angular_radius);
  232. // Determine phase factor of reflector as seen by observer
  233. const double observer_reflector_phase_factor = dot(observer_reflector_direction_eus, -reflector_blackbody_direction_eus) * 0.5 + 0.5;
  234. // Measure observer reference body solid angle as seen by reflector
  235. const double reflector_observer_angular_radius = astro::angular_radius(reference_body->radius, observer_reflector_distance);
  236. const double reflector_observer_solid_angle = geom::solid_angle::cone(reflector_observer_angular_radius);
  237. // Determine phase factor of observer reference body as by reflector
  238. const double reflector_observer_phase_factor = dot(-observer_reflector_direction_eus, -observer_blackbody_direction_eus) * 0.5 + 0.5;
  239. // Calculate spectral transmittance between observer and reflector factor due to atmospheric extinction
  240. double3 observer_reflector_transmittance = {1, 1, 1};
  241. if (reference_atmosphere)
  242. {
  243. const geom::ray<double> ray = {{0, 0, 0}, observer_reflector_direction_eus};
  244. observer_reflector_transmittance = integrate_transmittance(*observer, *reference_body, *reference_atmosphere, ray);
  245. }
  246. // Measure luminance of observer reference body as seen by reflector
  247. const double3 reflector_observer_luminance = observer_blackbody_illuminance * reference_body->albedo * observer_reflector_transmittance * reflector_observer_phase_factor * math::inv_pi<double>;
  248. // Measure illuminance from observer reference body reaching reflector
  249. const double3 reflector_observer_illuminance = reflector_observer_luminance * reflector_observer_solid_angle;
  250. // Measure luminance of reflector as seen by observer
  251. const double3 observer_reflector_luminance = (reflector_blackbody_illuminance * observer_reflector_phase_factor + reflector_observer_illuminance) * reflector.albedo * observer_reflector_transmittance * math::inv_pi<double>;
  252. // Measure illuminance from reflector reaching observer
  253. const double3 observer_reflector_illuminance = observer_reflector_luminance * observer_reflector_solid_angle;
  254. if (this->sky_pass)
  255. {
  256. this->sky_pass->set_moon_position(transform.local.translation);
  257. this->sky_pass->set_moon_rotation(transform.local.rotation);
  258. this->sky_pass->set_moon_angular_radius(static_cast<float>(observer_reflector_angular_radius));
  259. this->sky_pass->set_moon_sunlight_direction(float3(-reflector_blackbody_direction_eus));
  260. this->sky_pass->set_moon_sunlight_illuminance(float3(reflector_blackbody_illuminance * observer_reflector_transmittance));
  261. this->sky_pass->set_moon_planetlight_direction(float3(observer_reflector_direction_eus));
  262. this->sky_pass->set_moon_planetlight_illuminance(float3(reflector_observer_illuminance * observer_reflector_transmittance));
  263. this->sky_pass->set_moon_illuminance(float3(observer_reflector_illuminance / observer_reflector_transmittance), float3(observer_reflector_illuminance));
  264. }
  265. if (this->moon_light)
  266. {
  267. const float3 reflector_up_eus = float3(icrf_to_eus.r * double3{0, 0, 1});
  268. this->moon_light->set_color(float3(observer_reflector_illuminance));
  269. this->moon_light->set_rotation
  270. (
  271. math::look_rotation
  272. (
  273. float3(-observer_reflector_direction_eus),
  274. reflector_up_eus
  275. )
  276. );
  277. // Bounce moon light
  278. bounce_illuminance += std::max(0.0, math::dot(bounce_normal, -observer_reflector_direction_eus)) * observer_reflector_illuminance * bounce_albedo;
  279. }
  280. });
  281. });
  282. if (bounce_light)
  283. {
  284. bounce_light->set_color(float3(bounce_illuminance));
  285. }
  286. }
  287. void astronomy::set_time(double t)
  288. {
  289. time_days = t;
  290. time_centuries = time_days * physics::time::jd::centuries_per_day<double>;
  291. }
  292. void astronomy::set_time_scale(double scale)
  293. {
  294. time_scale = scale;
  295. }
  296. void astronomy::set_observer(entity::id eid)
  297. {
  298. if (observer_eid != eid)
  299. {
  300. observer_eid = eid;
  301. if (observer_eid != entt::null)
  302. observer_modified();
  303. else
  304. reference_body_eid = entt::null;
  305. }
  306. }
  307. void astronomy::set_transmittance_samples(std::size_t samples)
  308. {
  309. transmittance_samples = samples;
  310. }
  311. void astronomy::set_sun_light(scene::directional_light* light)
  312. {
  313. sun_light = light;
  314. }
  315. void astronomy::set_sky_light(scene::ambient_light* light)
  316. {
  317. sky_light = light;
  318. }
  319. void astronomy::set_moon_light(scene::directional_light* light)
  320. {
  321. moon_light = light;
  322. }
  323. void astronomy::set_bounce_light(scene::directional_light* light)
  324. {
  325. bounce_light = light;
  326. }
  327. void astronomy::set_bounce_albedo(const double3& albedo)
  328. {
  329. bounce_albedo = albedo;
  330. }
  331. void astronomy::set_starlight_illuminance(const double3& illuminance)
  332. {
  333. starlight_illuminance = illuminance;
  334. }
  335. void astronomy::set_sky_pass(::render::sky_pass* pass)
  336. {
  337. this->sky_pass = pass;
  338. if (sky_pass)
  339. {
  340. if (observer_eid != entt::null)
  341. {
  342. // Get pointer to observer
  343. const auto observer = registry.try_get<component::observer>(reference_body_eid);
  344. sky_pass->set_observer_elevation(static_cast<float>(observer->elevation));
  345. }
  346. if (reference_body_eid != entt::null)
  347. {
  348. // Get pointer to reference celestial body
  349. const auto reference_body = registry.try_get<component::celestial_body>(reference_body_eid);
  350. if (reference_body)
  351. sky_pass->set_planet_radius(static_cast<float>(reference_body->radius));
  352. else
  353. sky_pass->set_planet_radius(0.0f);
  354. }
  355. }
  356. }
  357. void astronomy::on_observer_modified(entity::registry& registry, entity::id entity_id)
  358. {
  359. if (entity_id == observer_eid)
  360. observer_modified();
  361. }
  362. void astronomy::on_observer_destroyed(entity::registry& registry, entity::id entity_id)
  363. {
  364. if (entity_id == observer_eid)
  365. observer_modified();
  366. }
  367. void astronomy::on_celestial_body_modified(entity::registry& registry, entity::id entity_id)
  368. {
  369. if (entity_id == reference_body_eid)
  370. reference_body_modified();
  371. }
  372. void astronomy::on_celestial_body_destroyed(entity::registry& registry, entity::id entity_id)
  373. {
  374. if (entity_id == reference_body_eid)
  375. reference_body_modified();
  376. }
  377. void astronomy::on_orbit_modified(entity::registry& registry, entity::id entity_id)
  378. {
  379. if (entity_id == reference_body_eid)
  380. reference_orbit_modified();
  381. }
  382. void astronomy::on_orbit_destroyed(entity::registry& registry, entity::id entity_id)
  383. {
  384. if (entity_id == reference_body_eid)
  385. reference_orbit_modified();
  386. }
  387. void astronomy::on_atmosphere_modified(entity::registry& registry, entity::id entity_id)
  388. {
  389. if (entity_id == reference_body_eid)
  390. reference_atmosphere_modified();
  391. }
  392. void astronomy::on_atmosphere_destroyed(entity::registry& registry, entity::id entity_id)
  393. {
  394. if (entity_id == reference_body_eid)
  395. reference_atmosphere_modified();
  396. }
  397. void astronomy::observer_modified()
  398. {
  399. // Get pointer to observer component
  400. const auto observer = registry.try_get<component::observer>(observer_eid);
  401. if (observer)
  402. {
  403. if (reference_body_eid != observer->reference_body_eid)
  404. {
  405. // Reference body changed
  406. reference_body_eid = observer->reference_body_eid;
  407. reference_body_modified();
  408. reference_orbit_modified();
  409. reference_atmosphere_modified();
  410. }
  411. if (reference_body_eid != entt::null)
  412. {
  413. // Get pointer to reference celestial body
  414. const auto reference_body = registry.try_get<component::celestial_body>(reference_body_eid);
  415. // Update BCBF to EUS transformation
  416. if (reference_body)
  417. update_bcbf_to_eus(*observer, *reference_body);
  418. }
  419. // Upload observer elevation to sky pass
  420. if (sky_pass)
  421. sky_pass->set_observer_elevation(static_cast<float>(observer->elevation));
  422. }
  423. }
  424. void astronomy::reference_body_modified()
  425. {
  426. // Get pointer to reference celestial body
  427. const auto reference_body = registry.try_get<component::celestial_body>(reference_body_eid);
  428. if (reference_body)
  429. {
  430. // Get pointer to observer
  431. const auto observer = registry.try_get<component::observer>(observer_eid);
  432. // Update BCBF to EUS transformation
  433. if (observer)
  434. update_bcbf_to_eus(*observer, *reference_body);
  435. }
  436. // Update reference celestial body-related sky pass parameters
  437. if (sky_pass)
  438. {
  439. if (reference_body)
  440. sky_pass->set_planet_radius(static_cast<float>(reference_body->radius));
  441. else
  442. sky_pass->set_planet_radius(0.0f);
  443. }
  444. }
  445. void astronomy::reference_orbit_modified()
  446. {
  447. }
  448. void astronomy::reference_atmosphere_modified()
  449. {
  450. }
  451. void astronomy::update_bcbf_to_eus(const game::component::observer& observer, const game::component::celestial_body& body)
  452. {
  453. // Construct BCBF to EUS transformation
  454. bcbf_to_eus = physics::orbit::frame::bcbf::to_enu
  455. (
  456. body.radius + observer.elevation,
  457. observer.latitude,
  458. observer.longitude
  459. ) * enu_to_eus;
  460. }
  461. void astronomy::update_icrf_to_eus(const game::component::celestial_body& body, const game::component::orbit& orbit)
  462. {
  463. // Evaluate reference body orientation polynomials
  464. const double body_pole_ra = math::polynomial::horner(body.pole_ra.begin(), body.pole_ra.end(), time_centuries);
  465. const double body_pole_dec = math::polynomial::horner(body.pole_dec.begin(), body.pole_dec.end(), time_centuries);
  466. const double body_prime_meridian = math::polynomial::horner(body.prime_meridian.begin(), body.prime_meridian.end(), time_days);
  467. // Construct ICRF frame to BCBF transformation
  468. math::transformation::se3<double> icrf_to_bcbf = physics::orbit::frame::bci::to_bcbf
  469. (
  470. body_pole_ra,
  471. body_pole_dec,
  472. body_prime_meridian
  473. );
  474. icrf_to_bcbf.t = icrf_to_bcbf.r * -orbit.position;
  475. /// Construct ICRF to EUS transformation
  476. icrf_to_eus = icrf_to_bcbf * bcbf_to_eus;
  477. // Pass ICRF to EUS transformation to sky pass
  478. if (sky_pass)
  479. {
  480. // Upload topocentric frame to sky pass
  481. sky_pass->set_icrf_to_eus
  482. (
  483. math::transformation::se3<float>
  484. {
  485. float3(icrf_to_eus.t),
  486. math::quaternion<float>(icrf_to_eus.r)
  487. }
  488. );
  489. }
  490. }
  491. double3 astronomy::integrate_transmittance(const game::component::observer& observer, const game::component::celestial_body& body, const game::component::atmosphere& atmosphere, geom::ray<double> ray) const
  492. {
  493. double3 transmittance = {1, 1, 1};
  494. // Make ray height relative to center of reference body
  495. ray.origin.y() += body.radius + observer.elevation;
  496. // Construct sphere representing upper limit of the atmosphere
  497. geom::sphere<double> atmosphere_sphere;
  498. atmosphere_sphere.center = {0, 0, 0};
  499. atmosphere_sphere.radius = body.radius + atmosphere.upper_limit;
  500. // Check for intersection between the ray and atmosphere
  501. auto intersection = geom::ray_sphere_intersection(ray, atmosphere_sphere);
  502. if (std::get<0>(intersection))
  503. {
  504. // Get point of intersection
  505. const double3 intersection_point = ray.extrapolate(std::get<2>(intersection));
  506. // Integrate optical of Rayleigh, Mie, and ozone particles
  507. const double optical_depth_r = physics::gas::atmosphere::optical_depth_exp(ray.origin, intersection_point, body.radius, atmosphere.rayleigh_scale_height, transmittance_samples);
  508. const double optical_depth_m = physics::gas::atmosphere::optical_depth_exp(ray.origin, intersection_point, body.radius, atmosphere.mie_scale_height, transmittance_samples);
  509. const double optical_depth_o = physics::gas::atmosphere::optical_depth_tri(ray.origin, intersection_point, body.radius, atmosphere.ozone_lower_limit, atmosphere.ozone_upper_limit, atmosphere.ozone_mode, transmittance_samples);
  510. // Calculate transmittance factor due to scattering and absorption
  511. const double3 extinction_r = atmosphere.rayleigh_scattering * optical_depth_r;
  512. const double extinction_m = atmosphere.mie_extinction * optical_depth_m;
  513. const double3 extinction_o = atmosphere.ozone_absorption * optical_depth_o;
  514. transmittance = extinction_r + double3{extinction_m, extinction_m, extinction_m} + extinction_o;
  515. transmittance.x() = std::exp(-transmittance.x());
  516. transmittance.y() = std::exp(-transmittance.y());
  517. transmittance.z() = std::exp(-transmittance.z());
  518. }
  519. return transmittance;
  520. }
  521. } // namespace system
  522. } // namespace game