Browse Source

Re-add celestial body component, separate blackbody functionality out of astronomy system into new blackbody system

master
C. J. Howard 2 years ago
parent
commit
bfcd5f14e2
10 changed files with 296 additions and 118 deletions
  1. +0
    -1
      CMakeLists.txt
  2. +0
    -3
      src/ecs/components/blackbody-component.hpp
  3. +43
    -0
      src/ecs/components/celestial-body-component.hpp
  4. +46
    -89
      src/ecs/systems/astronomy-system.cpp
  5. +14
    -20
      src/ecs/systems/astronomy-system.hpp
  6. +111
    -0
      src/ecs/systems/blackbody-system.cpp
  7. +59
    -0
      src/ecs/systems/blackbody-system.hpp
  8. +5
    -0
      src/game/bootloader.cpp
  9. +2
    -0
      src/game/game-context.hpp
  10. +16
    -5
      src/game/states/play-state.cpp

+ 0
- 1
CMakeLists.txt View File

@ -1,6 +1,5 @@
cmake_minimum_required(VERSION 3.7) cmake_minimum_required(VERSION 3.7)
option(VERSION_STRING "Project version string" "0.0.0") option(VERSION_STRING "Project version string" "0.0.0")
project(antkeeper VERSION ${VERSION_STRING} LANGUAGES CXX) project(antkeeper VERSION ${VERSION_STRING} LANGUAGES CXX)

+ 0
- 3
src/ecs/components/blackbody-component.hpp View File

@ -28,9 +28,6 @@ struct blackbody_component
/// Effective temperature, in Kelvin. /// Effective temperature, in Kelvin.
double temperature; double temperature;
/// Blackbody radius, in meters.
double radius;
/// (Dependent) RGB luminous intensity, in candela. /// (Dependent) RGB luminous intensity, in candela.
double3 luminous_intensity; double3 luminous_intensity;
}; };

+ 43
- 0
src/ecs/components/celestial-body-component.hpp View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_ECS_CELESTIAL_BODY_COMPONENT_HPP
#define ANTKEEPER_ECS_CELESTIAL_BODY_COMPONENT_HPP
namespace ecs {
/// A simple celestial body.
struct celestial_body_component
{
/// Body radius, in meters.
double radius;
/// Angle between the body's rotational axis and its orbital axis, in radians.
double axial_tilt;
/// Angle of rotation about the body's rotational axis at epoch, in radians.
double axial_rotation;
/// Angular frequency, in radians per day.
double angular_frequency;
};
} // namespace ecs
#endif // ANTKEEPER_ECS_CELESTIAL_BODY_COMPONENT_HPP

+ 46
- 89
src/ecs/systems/astronomy-system.cpp View File

@ -19,20 +19,16 @@
#include "ecs/systems/astronomy-system.hpp" #include "ecs/systems/astronomy-system.hpp"
#include "astro/apparent-size.hpp" #include "astro/apparent-size.hpp"
#include "ecs/components/orbit-component.hpp"
#include "ecs/components/blackbody-component.hpp" #include "ecs/components/blackbody-component.hpp"
#include "ecs/components/atmosphere-component.hpp"
#include "ecs/components/transform-component.hpp" #include "ecs/components/transform-component.hpp"
#include "geom/intersection.hpp" #include "geom/intersection.hpp"
#include "color/color.hpp" #include "color/color.hpp"
#include "physics/orbit/orbit.hpp" #include "physics/orbit/orbit.hpp"
#include "physics/time/ut1.hpp" #include "physics/time/ut1.hpp"
#include "physics/light/blackbody.hpp"
#include "physics/light/photometry.hpp" #include "physics/light/photometry.hpp"
#include "physics/light/luminosity.hpp" #include "physics/light/luminosity.hpp"
#include "physics/light/refraction.hpp" #include "physics/light/refraction.hpp"
#include "physics/atmosphere.hpp" #include "physics/atmosphere.hpp"
#include "math/quadrature.hpp"
#include "geom/cartesian.hpp" #include "geom/cartesian.hpp"
#include <iostream> #include <iostream>
@ -57,9 +53,10 @@ astronomy_system::astronomy_system(ecs::registry& registry):
entity_system(registry), entity_system(registry),
universal_time(0.0), universal_time(0.0),
time_scale(1.0), time_scale(1.0),
reference_body(entt::null),
reference_body_axial_tilt(0.0),
reference_body_axial_rotation(0.0),
reference_entity(entt::null),
reference_orbit(nullptr),
reference_body(nullptr),
reference_atmosphere(nullptr),
sun_light(nullptr), sun_light(nullptr),
sky_pass(nullptr) sky_pass(nullptr)
{ {
@ -67,9 +64,6 @@ astronomy_system::astronomy_system(ecs::registry& registry):
rgb_wavelengths_nm = {602.224, 541.069, 448.143}; rgb_wavelengths_nm = {602.224, 541.069, 448.143};
rgb_wavelengths_m = rgb_wavelengths_nm * 1e-9; rgb_wavelengths_m = rgb_wavelengths_nm * 1e-9;
registry.on_construct<ecs::blackbody_component>().connect<&astronomy_system::on_blackbody_construct>(this);
registry.on_replace<ecs::blackbody_component>().connect<&astronomy_system::on_blackbody_replace>(this);
registry.on_construct<ecs::atmosphere_component>().connect<&astronomy_system::on_atmosphere_construct>(this); registry.on_construct<ecs::atmosphere_component>().connect<&astronomy_system::on_atmosphere_construct>(this);
registry.on_replace<ecs::atmosphere_component>().connect<&astronomy_system::on_atmosphere_replace>(this); registry.on_replace<ecs::atmosphere_component>().connect<&astronomy_system::on_atmosphere_replace>(this);
} }
@ -79,35 +73,28 @@ void astronomy_system::update(double t, double dt)
// Add scaled timestep to current time // Add scaled timestep to current time
set_universal_time(universal_time + dt * time_scale); set_universal_time(universal_time + dt * time_scale);
// Abort if reference body has not been set
if (reference_body == entt::null)
return;
// Abort if reference body has no orbit component
if (!registry.has<ecs::orbit_component>(reference_body))
// Abort if either reference body or orbit have not been set
if (!reference_orbit || !reference_body)
return; return;
// Update axial rotation of reference body
reference_body_axial_rotation = physics::time::ut1::era(universal_time);
// Get orbit component of reference body
const auto& reference_orbit = registry.get<ecs::orbit_component>(reference_body);
// Determine axial rotation at current time
const double reference_axial_rotation = reference_body->axial_rotation + reference_body->angular_frequency * universal_time;
/// Construct reference frame which transforms coordinates from inertial space to reference body BCBF space
// Construct reference frame which transforms coordinates from inertial space to reference body BCBF space
inertial_to_bcbf = physics::orbit::inertial::to_bcbf inertial_to_bcbf = physics::orbit::inertial::to_bcbf
( (
reference_orbit.state.r,
reference_orbit.elements.i,
reference_body_axial_tilt,
reference_body_axial_rotation
reference_orbit->state.r,
reference_orbit->elements.i,
reference_body->axial_tilt,
reference_axial_rotation
); );
/// Construct reference frame which transforms coordinates from inertial space to reference body topocentric space
// Construct reference frame which transforms coordinates from inertial space to reference body topocentric space
inertial_to_topocentric = inertial_to_bcbf * bcbf_to_topocentric; inertial_to_topocentric = inertial_to_bcbf * bcbf_to_topocentric;
// Set the transform component translations of orbiting bodies to their topocentric positions // Set the transform component translations of orbiting bodies to their topocentric positions
registry.view<orbit_component, transform_component>().each( registry.view<orbit_component, transform_component>().each(
[&](ecs::entity entity, auto& orbit, auto& transform)
[&](ecs::entity entity, const auto& orbit, auto& transform)
{ {
// Transform Cartesian position vector (r) from inertial space to topocentric space // Transform Cartesian position vector (r) from inertial space to topocentric space
const math::vector3<double> r_topocentric = inertial_to_topocentric * orbit.state.r; const math::vector3<double> r_topocentric = inertial_to_topocentric * orbit.state.r;
@ -116,14 +103,12 @@ void astronomy_system::update(double t, double dt)
transform.local.translation = math::type_cast<float>(r_topocentric); transform.local.translation = math::type_cast<float>(r_topocentric);
}); });
const double earth_radius = 6.3781e6;
// Update blackbody lighting // Update blackbody lighting
registry.view<blackbody_component, orbit_component>().each(
[&](ecs::entity entity, auto& blackbody, auto& orbit)
registry.view<celestial_body_component, orbit_component, blackbody_component>().each(
[&](ecs::entity entity, const auto& celestial_body, const auto& orbit, const auto& blackbody)
{ {
// Calculate blackbody inertial basis // Calculate blackbody inertial basis
double3 blackbody_forward_inertial = math::normalize(reference_orbit.state.r - orbit.state.r);
double3 blackbody_forward_inertial = math::normalize(reference_orbit->state.r - orbit.state.r);
double3 blackbody_up_inertial = {0, 0, 1}; double3 blackbody_up_inertial = {0, 0, 1};
// Transform blackbody inertial position and basis into topocentric space // Transform blackbody inertial position and basis into topocentric space
@ -141,10 +126,8 @@ void astronomy_system::update(double t, double dt)
double3 atmospheric_transmittance = {1.0, 1.0, 1.0}; double3 atmospheric_transmittance = {1.0, 1.0, 1.0};
// Get atmosphere component of reference body (if any) // Get atmosphere component of reference body (if any)
if (this->registry.has<ecs::atmosphere_component>(reference_body))
if (reference_atmosphere)
{ {
const ecs::atmosphere_component& atmosphere = this->registry.get<ecs::atmosphere_component>(reference_body);
// Altitude of observer in meters // Altitude of observer in meters
geom::ray<double> sample_ray; geom::ray<double> sample_ray;
sample_ray.origin = {0, observer_location[0], 0}; sample_ray.origin = {0, observer_location[0], 0};
@ -152,7 +135,7 @@ void astronomy_system::update(double t, double dt)
geom::sphere<double> exosphere; geom::sphere<double> exosphere;
exosphere.center = {0, 0, 0}; exosphere.center = {0, 0, 0};
exosphere.radius = earth_radius + atmosphere.exosphere_altitude;
exosphere.radius = reference_body->radius + reference_atmosphere->exosphere_altitude;
auto intersection_result = geom::ray_sphere_intersection(sample_ray, exosphere); auto intersection_result = geom::ray_sphere_intersection(sample_ray, exosphere);
@ -161,11 +144,11 @@ void astronomy_system::update(double t, double dt)
double3 sample_start = sample_ray.origin; double3 sample_start = sample_ray.origin;
double3 sample_end = sample_ray.extrapolate(std::get<2>(intersection_result)); double3 sample_end = sample_ray.extrapolate(std::get<2>(intersection_result));
double optical_depth_r = physics::atmosphere::optical_depth(sample_start, sample_end, earth_radius, atmosphere.rayleigh_scale_height, 32);
double optical_depth_k = physics::atmosphere::optical_depth(sample_start, sample_end, earth_radius, atmosphere.mie_scale_height, 32);
double optical_depth_r = physics::atmosphere::optical_depth(sample_start, sample_end, reference_body->radius, reference_atmosphere->rayleigh_scale_height, 32);
double optical_depth_k = physics::atmosphere::optical_depth(sample_start, sample_end, reference_body->radius, reference_atmosphere->mie_scale_height, 32);
double optical_depth_o = 0.0; double optical_depth_o = 0.0;
atmospheric_transmittance = transmittance(optical_depth_r, optical_depth_k, optical_depth_o, atmosphere.rayleigh_scattering, atmosphere.mie_scattering);
atmospheric_transmittance = transmittance(optical_depth_r, optical_depth_k, optical_depth_o, reference_atmosphere->rayleigh_scattering, reference_atmosphere->mie_scattering);
} }
} }
@ -192,7 +175,7 @@ void astronomy_system::update(double t, double dt)
this->sky_pass->set_sun_position(math::type_cast<float>(blackbody_position_topocentric)); this->sky_pass->set_sun_position(math::type_cast<float>(blackbody_position_topocentric));
this->sky_pass->set_sun_color(math::type_cast<float>(blackbody.luminous_intensity * distance_attenuation)); this->sky_pass->set_sun_color(math::type_cast<float>(blackbody.luminous_intensity * distance_attenuation));
double blackbody_angular_radius = std::asin((blackbody.radius * 2.0) / (blackbody_distance * 2.0));
double blackbody_angular_radius = std::asin((celestial_body.radius * 2.0) / (blackbody_distance * 2.0));
this->sky_pass->set_sun_angular_radius(static_cast<float>(blackbody_angular_radius)); this->sky_pass->set_sun_angular_radius(static_cast<float>(blackbody_angular_radius));
} }
} }
@ -212,18 +195,16 @@ void astronomy_system::update(double t, double dt)
); );
// Upload observer altitude to sky pass // Upload observer altitude to sky pass
float observer_altitude = observer_location[0] - earth_radius;
float observer_altitude = observer_location[0] - reference_body->radius;
sky_pass->set_observer_altitude(observer_altitude); sky_pass->set_observer_altitude(observer_altitude);
// Upload atmosphere params to sky pass // Upload atmosphere params to sky pass
if (this->registry.has<ecs::atmosphere_component>(reference_body))
if (reference_atmosphere)
{ {
const ecs::atmosphere_component& atmosphere = this->registry.get<ecs::atmosphere_component>(reference_body);
sky_pass->set_scale_heights(atmosphere.rayleigh_scale_height, atmosphere.mie_scale_height);
sky_pass->set_scattering_coefficients(math::type_cast<float>(atmosphere.rayleigh_scattering), math::type_cast<float>(atmosphere.mie_scattering));
sky_pass->set_mie_anisotropy(atmosphere.mie_anisotropy);
sky_pass->set_atmosphere_radii(earth_radius, earth_radius + atmosphere.exosphere_altitude);
sky_pass->set_scale_heights(reference_atmosphere->rayleigh_scale_height, reference_atmosphere->mie_scale_height);
sky_pass->set_scattering_coefficients(math::type_cast<float>(reference_atmosphere->rayleigh_scattering), math::type_cast<float>(reference_atmosphere->mie_scattering));
sky_pass->set_mie_anisotropy(reference_atmosphere->mie_anisotropy);
sky_pass->set_atmosphere_radii(reference_body->radius, reference_body->radius + reference_atmosphere->exosphere_altitude);
} }
} }
} }
@ -240,12 +221,22 @@ void astronomy_system::set_time_scale(double scale)
void astronomy_system::set_reference_body(ecs::entity entity) void astronomy_system::set_reference_body(ecs::entity entity)
{ {
reference_body = entity;
}
void astronomy_system::set_reference_body_axial_tilt(double angle)
{
reference_body_axial_tilt = angle;
reference_entity = entity;
reference_orbit = nullptr;
reference_body = nullptr;
reference_atmosphere = nullptr;
if (reference_entity != entt::null)
{
if (registry.has<ecs::orbit_component>(reference_entity))
reference_orbit = &registry.get<ecs::orbit_component>(reference_entity);
if (registry.has<ecs::celestial_body_component>(reference_entity))
reference_body = &registry.get<ecs::celestial_body_component>(reference_entity);
if (registry.has<ecs::atmosphere_component>(reference_entity))
reference_atmosphere = &registry.get<ecs::atmosphere_component>(reference_entity);
}
} }
void astronomy_system::set_observer_location(const double3& location) void astronomy_system::set_observer_location(const double3& location)
@ -285,40 +276,6 @@ void astronomy_system::set_sky_pass(::sky_pass* pass)
this->sky_pass = pass; this->sky_pass = pass;
} }
void astronomy_system::on_blackbody_construct(ecs::registry& registry, ecs::entity entity, ecs::blackbody_component& blackbody)
{
on_blackbody_replace(registry, entity, blackbody);
}
void astronomy_system::on_blackbody_replace(ecs::registry& registry, ecs::entity entity, ecs::blackbody_component& blackbody)
{
// Calculate the surface area of a spherical blackbody
const double surface_area = 4.0 * math::pi<double> * blackbody.radius * blackbody.radius;
// Construct a lambda function which calculates the blackbody's RGB luminous intensity of a given wavelength
auto rgb_luminous_intensity = [blackbody, surface_area](double wavelength_nm) -> double3
{
// Convert wavelength from nanometers to meters
const double wavelength_m = wavelength_nm * 1e-9;
// Calculate the spectral intensity of the wavelength
const double spectral_intensity = physics::light::blackbody::spectral_intensity<double>(blackbody.temperature, surface_area, wavelength_m);
// Calculate the ACEScg color of the wavelength using CIE color matching functions
double3 spectral_color = color::xyz::to_acescg(color::xyz::match(wavelength_nm));
// Scale the spectral color by spectral intensity
return spectral_color * spectral_intensity * 1e-9 * physics::light::max_luminous_efficacy<double>;
};
// Construct a range of sample wavelengths in the visible spectrum
std::vector<double> samples(780 - 280);
std::iota(samples.begin(), samples.end(), 280);
// Integrate the blackbody RGB luminous intensity over wavelengths in the visible spectrum
blackbody.luminous_intensity = math::quadrature::simpson(rgb_luminous_intensity, samples.begin(), samples.end());
}
void astronomy_system::on_atmosphere_construct(ecs::registry& registry, ecs::entity entity, ecs::atmosphere_component& atmosphere) void astronomy_system::on_atmosphere_construct(ecs::registry& registry, ecs::entity entity, ecs::atmosphere_component& atmosphere)
{ {
on_atmosphere_replace(registry, entity, atmosphere); on_atmosphere_replace(registry, entity, atmosphere);

+ 14
- 20
src/ecs/systems/astronomy-system.hpp View File

@ -26,8 +26,9 @@
#include "utility/fundamental-types.hpp" #include "utility/fundamental-types.hpp"
#include "physics/frame.hpp" #include "physics/frame.hpp"
#include "renderer/passes/sky-pass.hpp" #include "renderer/passes/sky-pass.hpp"
#include "ecs/components/blackbody-component.hpp"
#include "ecs/components/atmosphere-component.hpp" #include "ecs/components/atmosphere-component.hpp"
#include "ecs/components/celestial-body-component.hpp"
#include "ecs/components/orbit-component.hpp"
namespace ecs { namespace ecs {
@ -63,19 +64,12 @@ public:
void set_time_scale(double scale); void set_time_scale(double scale);
/** /**
* Sets the reference body, from which observations are taking place.
* Sets the reference body entity, from which observations are taking place.
* *
* @param entity Entity of the reference body. * @param entity Entity of the reference body.
*/ */
void set_reference_body(ecs::entity entity); void set_reference_body(ecs::entity entity);
/**
* Sets the axial tilt of the reference body.
*
* @param angle Angle between the reference body's rotational axis and its orbital axis, in radians.
*/
void set_reference_body_axial_tilt(double angle);
/** /**
* Sets the location of the observer using spherical coordinates in BCBF space. * Sets the location of the observer using spherical coordinates in BCBF space.
* *
@ -88,21 +82,21 @@ public:
void set_sky_pass(sky_pass* pass); void set_sky_pass(sky_pass* pass);
private: private:
void on_blackbody_construct(ecs::registry& registry, ecs::entity entity, ecs::blackbody_component& blackbody);
void on_blackbody_replace(ecs::registry& registry, ecs::entity entity, ecs::blackbody_component& blackbody);
void on_atmosphere_construct(ecs::registry& registry, ecs::entity entity, ecs::atmosphere_component& atmosphere); void on_atmosphere_construct(ecs::registry& registry, ecs::entity entity, ecs::atmosphere_component& atmosphere);
void on_atmosphere_replace(ecs::registry& registry, ecs::entity entity, ecs::atmosphere_component& atmosphere); void on_atmosphere_replace(ecs::registry& registry, ecs::entity entity, ecs::atmosphere_component& atmosphere);
double universal_time; double universal_time;
double time_scale; double time_scale;
ecs::entity reference_body;
double reference_body_axial_tilt;
double reference_body_axial_rotation;
double3 rgb_wavelengths_nm;
double3 rgb_wavelengths_m;
ecs::entity reference_entity;
const ecs::orbit_component* reference_orbit;
const ecs::celestial_body_component* reference_body;
const ecs::atmosphere_component* reference_atmosphere;
double3 observer_location; double3 observer_location;
scene::directional_light* sun_light;
sky_pass* sky_pass;
physics::frame<double> inertial_to_bcbf; physics::frame<double> inertial_to_bcbf;
physics::frame<double> bcbf_to_topocentric; physics::frame<double> bcbf_to_topocentric;
@ -110,8 +104,8 @@ private:
physics::frame<double> sez_to_ezs; physics::frame<double> sez_to_ezs;
physics::frame<double> ezs_to_sez; physics::frame<double> ezs_to_sez;
double3 rgb_wavelengths_nm;
double3 rgb_wavelengths_m;
scene::directional_light* sun_light;
sky_pass* sky_pass;
}; };
} // namespace ecs } // namespace ecs

+ 111
- 0
src/ecs/systems/blackbody-system.cpp View File

@ -0,0 +1,111 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ecs/systems/blackbody-system.hpp"
#include "color/color.hpp"
#include "physics/light/blackbody.hpp"
#include "physics/light/photometry.hpp"
#include "math/quadrature.hpp"
namespace ecs {
blackbody_system::blackbody_system(ecs::registry& registry):
entity_system(registry)
{
// RGB wavelengths determined by matching wavelengths to XYZ, transforming XYZ to ACEScg, then selecting the max wavelengths for R, G, and B.
rgb_wavelengths_nm = {602.224, 541.069, 448.143};
rgb_wavelengths_m = rgb_wavelengths_nm * 1e-9;
// Construct a range of sample wavelengths in the visible spectrum
visible_wavelengths_nm.resize(780 - 280);
std::iota(visible_wavelengths_nm.begin(), visible_wavelengths_nm.end(), 280);
registry.on_construct<ecs::blackbody_component>().connect<&blackbody_system::on_blackbody_construct>(this);
registry.on_replace<ecs::blackbody_component>().connect<&blackbody_system::on_blackbody_replace>(this);
registry.on_construct<ecs::celestial_body_component>().connect<&blackbody_system::on_celestial_body_construct>(this);
registry.on_replace<ecs::celestial_body_component>().connect<&blackbody_system::on_celestial_body_replace>(this);
}
void blackbody_system::update(double t, double dt)
{}
void blackbody_system::update_luminous_intensity(ecs::entity entity)
{
// Abort if entity has no blackbody component
if (!registry.has<blackbody_component>(entity))
return;
// Get blackbody component of the entity
blackbody_component& blackbody = registry.get<blackbody_component>(entity);
// Clear luminous intensity
blackbody.luminous_intensity = {0, 0, 0};
// Abort if entity has no celestial body component
if (!registry.has<celestial_body_component>(entity))
return;
// Get celestial body component of the entity
const celestial_body_component& celestial_body = registry.get<celestial_body_component>(entity);
// Calculate (spherical) surface area of the celestial body
const double surface_area = 4.0 * math::pi<double> * celestial_body.radius * celestial_body.radius;
// Construct a lambda function which calculates the blackbody's RGB luminous intensity of a given wavelength
auto rgb_luminous_intensity = [blackbody, surface_area](double wavelength_nm) -> double3
{
// Convert wavelength from nanometers to meters
const double wavelength_m = wavelength_nm * 1e-9;
// Calculate the spectral intensity of the wavelength
const double spectral_intensity = physics::light::blackbody::spectral_intensity<double>(blackbody.temperature, surface_area, wavelength_m);
// Calculate the ACEScg color of the wavelength using CIE color matching functions
double3 spectral_color = color::xyz::to_acescg(color::xyz::match(wavelength_nm));
// Scale the spectral color by spectral intensity
return spectral_color * spectral_intensity * 1e-9 * physics::light::max_luminous_efficacy<double>;
};
// Integrate the blackbody RGB luminous intensity over wavelengths in the visible spectrum
blackbody.luminous_intensity = math::quadrature::simpson(rgb_luminous_intensity, visible_wavelengths_nm.begin(), visible_wavelengths_nm.end());
}
void blackbody_system::on_blackbody_construct(ecs::registry& registry, ecs::entity entity, ecs::blackbody_component& blackbody)
{
update_luminous_intensity(entity);
}
void blackbody_system::on_blackbody_replace(ecs::registry& registry, ecs::entity entity, ecs::blackbody_component& blackbody)
{
update_luminous_intensity(entity);
}
void blackbody_system::on_celestial_body_construct(ecs::registry& registry, ecs::entity entity, ecs::celestial_body_component& celestial_body)
{
update_luminous_intensity(entity);
}
void blackbody_system::on_celestial_body_replace(ecs::registry& registry, ecs::entity entity, ecs::celestial_body_component& celestial_body)
{
update_luminous_intensity(entity);
}
} // namespace ecs

+ 59
- 0
src/ecs/systems/blackbody-system.hpp View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_ECS_BLACKBODY_SYSTEM_HPP
#define ANTKEEPER_ECS_BLACKBODY_SYSTEM_HPP
#include "entity-system.hpp"
#include "ecs/entity.hpp"
#include "utility/fundamental-types.hpp"
#include "ecs/components/blackbody-component.hpp"
#include "ecs/components/celestial-body-component.hpp"
#include <vector>
namespace ecs {
/**
* Calculates the RGB luminous intensity of blackbody radiators.
*/
class blackbody_system:
public entity_system
{
public:
blackbody_system(ecs::registry& registry);
virtual void update(double t, double dt);
private:
void update_luminous_intensity(ecs::entity entity);
void on_blackbody_construct(ecs::registry& registry, ecs::entity entity, ecs::blackbody_component& blackbody);
void on_blackbody_replace(ecs::registry& registry, ecs::entity entity, ecs::blackbody_component& blackbody);
void on_celestial_body_construct(ecs::registry& registry, ecs::entity entity, ecs::celestial_body_component& celestial_body);
void on_celestial_body_replace(ecs::registry& registry, ecs::entity entity, ecs::celestial_body_component& celestial_body);
double3 rgb_wavelengths_nm;
double3 rgb_wavelengths_m;
std::vector<double> visible_wavelengths_nm;
};
} // namespace ecs
#endif // ANTKEEPER_ECS_BLACKBODY_SYSTEM_HPP

+ 5
- 0
src/game/bootloader.cpp View File

@ -74,6 +74,7 @@
#include "ecs/systems/tracking-system.hpp" #include "ecs/systems/tracking-system.hpp"
#include "ecs/systems/painting-system.hpp" #include "ecs/systems/painting-system.hpp"
#include "ecs/systems/astronomy-system.hpp" #include "ecs/systems/astronomy-system.hpp"
#include "ecs/systems/blackbody-system.hpp"
#include "ecs/systems/orbit-system.hpp" #include "ecs/systems/orbit-system.hpp"
#include "ecs/components/marker-component.hpp" #include "ecs/components/marker-component.hpp"
#include "ecs/commands.hpp" #include "ecs/commands.hpp"
@ -860,6 +861,9 @@ void setup_systems(game_context* ctx)
// Setup solar system // Setup solar system
ctx->orbit_system = new ecs::orbit_system(*ctx->ecs_registry); ctx->orbit_system = new ecs::orbit_system(*ctx->ecs_registry);
// Setup blackbody system
ctx->blackbody_system = new ecs::blackbody_system(*ctx->ecs_registry);
// Setup astronomy system // Setup astronomy system
ctx->astronomy_system = new ecs::astronomy_system(*ctx->ecs_registry); ctx->astronomy_system = new ecs::astronomy_system(*ctx->ecs_registry);
@ -1255,6 +1259,7 @@ void setup_callbacks(game_context* ctx)
ctx->tool_system->update(t, dt); ctx->tool_system->update(t, dt);
ctx->orbit_system->update(t, dt); ctx->orbit_system->update(t, dt);
ctx->blackbody_system->update(t, dt);
ctx->astronomy_system->update(t, dt); ctx->astronomy_system->update(t, dt);
ctx->spatial_system->update(t, dt); ctx->spatial_system->update(t, dt);
ctx->constraint_system->update(t, dt); ctx->constraint_system->update(t, dt);

+ 2
- 0
src/game/game-context.hpp View File

@ -83,6 +83,7 @@ namespace ecs
class tracking_system; class tracking_system;
class painting_system; class painting_system;
class astronomy_system; class astronomy_system;
class blackbody_system;
class orbit_system; class orbit_system;
class behavior_system; class behavior_system;
class collision_system; class collision_system;
@ -247,6 +248,7 @@ struct game_context
ecs::spatial_system* spatial_system; ecs::spatial_system* spatial_system;
ecs::tracking_system* tracking_system; ecs::tracking_system* tracking_system;
ecs::painting_system* painting_system; ecs::painting_system* painting_system;
ecs::blackbody_system* blackbody_system;
ecs::astronomy_system* astronomy_system; ecs::astronomy_system* astronomy_system;
ecs::orbit_system* orbit_system; ecs::orbit_system* orbit_system;

+ 16
- 5
src/game/states/play-state.cpp View File

@ -33,6 +33,7 @@
#include "ecs/components/camera-follow-component.hpp" #include "ecs/components/camera-follow-component.hpp"
#include "ecs/components/orbit-component.hpp" #include "ecs/components/orbit-component.hpp"
#include "ecs/components/blackbody-component.hpp" #include "ecs/components/blackbody-component.hpp"
#include "ecs/components/celestial-body-component.hpp"
#include "ecs/components/atmosphere-component.hpp" #include "ecs/components/atmosphere-component.hpp"
#include "ecs/components/light-component.hpp" #include "ecs/components/light-component.hpp"
#include "ecs/commands.hpp" #include "ecs/commands.hpp"
@ -96,6 +97,12 @@ void play_state_enter(game_context* ctx)
// Create sun // Create sun
auto sun_entity = ecs_registry.create(); auto sun_entity = ecs_registry.create();
{ {
ecs::celestial_body_component body;
body.radius = 6.957e+8;
body.axial_tilt = math::radians(0.0);
body.axial_rotation = math::radians(0.0);
body.angular_frequency = math::radians(0.0);
ecs::orbit_component orbit; ecs::orbit_component orbit;
orbit.elements.a = 0.0; orbit.elements.a = 0.0;
orbit.elements.e = 0.0; orbit.elements.e = 0.0;
@ -106,12 +113,12 @@ void play_state_enter(game_context* ctx)
ecs::blackbody_component blackbody; ecs::blackbody_component blackbody;
blackbody.temperature = 5777.0; blackbody.temperature = 5777.0;
blackbody.radius = 6.957e+8;
ecs::transform_component transform; ecs::transform_component transform;
transform.local = math::identity_transform<float>; transform.local = math::identity_transform<float>;
transform.warp = true; transform.warp = true;
ecs_registry.assign<ecs::celestial_body_component>(sun_entity, body);
ecs_registry.assign<ecs::orbit_component>(sun_entity, orbit); ecs_registry.assign<ecs::orbit_component>(sun_entity, orbit);
ecs_registry.assign<ecs::blackbody_component>(sun_entity, blackbody); ecs_registry.assign<ecs::blackbody_component>(sun_entity, blackbody);
ecs_registry.assign<ecs::transform_component>(sun_entity, transform); ecs_registry.assign<ecs::transform_component>(sun_entity, transform);
@ -120,6 +127,12 @@ void play_state_enter(game_context* ctx)
// Create Earth // Create Earth
auto earth_entity = ecs_registry.create(); auto earth_entity = ecs_registry.create();
{ {
ecs::celestial_body_component body;
body.radius = 6.3781e6;
body.axial_tilt = math::radians(23.4393);
body.axial_rotation = math::radians(280.46061837504);
body.angular_frequency = math::radians(360.9856122880876128);
ecs::orbit_component orbit; ecs::orbit_component orbit;
orbit.elements.a = 1.496e+11; orbit.elements.a = 1.496e+11;
orbit.elements.e = 0.01671123; orbit.elements.e = 0.01671123;
@ -131,12 +144,10 @@ void play_state_enter(game_context* ctx)
ecs::atmosphere_component atmosphere; ecs::atmosphere_component atmosphere;
atmosphere.exosphere_altitude = 65e3; atmosphere.exosphere_altitude = 65e3;
atmosphere.index_of_refraction = 1.000293; atmosphere.index_of_refraction = 1.000293;
atmosphere.rayleigh_density = 2.545e25; atmosphere.rayleigh_density = 2.545e25;
atmosphere.mie_density = 14.8875;
atmosphere.rayleigh_scale_height = 8000.0; atmosphere.rayleigh_scale_height = 8000.0;
atmosphere.mie_density = 14.8875;
atmosphere.mie_scale_height = 1200.0; atmosphere.mie_scale_height = 1200.0;
atmosphere.mie_anisotropy = 0.8; atmosphere.mie_anisotropy = 0.8;
@ -144,6 +155,7 @@ void play_state_enter(game_context* ctx)
transform.local = math::identity_transform<float>; transform.local = math::identity_transform<float>;
transform.warp = true; transform.warp = true;
ecs_registry.assign<ecs::celestial_body_component>(earth_entity, body);
ecs_registry.assign<ecs::orbit_component>(earth_entity, orbit); ecs_registry.assign<ecs::orbit_component>(earth_entity, orbit);
ecs_registry.assign<ecs::atmosphere_component>(earth_entity, atmosphere); ecs_registry.assign<ecs::atmosphere_component>(earth_entity, atmosphere);
ecs_registry.assign<ecs::transform_component>(earth_entity, transform); ecs_registry.assign<ecs::transform_component>(earth_entity, transform);
@ -172,7 +184,6 @@ void play_state_enter(game_context* ctx)
// Set astronomy system observation parameters // Set astronomy system observation parameters
ctx->astronomy_system->set_reference_body(earth_entity); ctx->astronomy_system->set_reference_body(earth_entity);
ctx->astronomy_system->set_reference_body_axial_tilt(math::radians(23.4393));
ctx->astronomy_system->set_observer_location(double3{6.3781e6, math::radians(0.0f), math::radians(0.0f)}); ctx->astronomy_system->set_observer_location(double3{6.3781e6, math::radians(0.0f), math::radians(0.0f)});
ctx->astronomy_system->set_sun_light(sun); ctx->astronomy_system->set_sun_light(sun);
ctx->astronomy_system->set_sky_pass(ctx->overworld_sky_pass); ctx->astronomy_system->set_sky_pass(ctx->overworld_sky_pass);

Loading…
Cancel
Save