diff --git a/CMakeLists.txt b/CMakeLists.txt index 7faddc1..8b94016 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,5 @@ cmake_minimum_required(VERSION 3.7) - option(VERSION_STRING "Project version string" "0.0.0") project(antkeeper VERSION ${VERSION_STRING} LANGUAGES CXX) diff --git a/src/astro/apparent-size.hpp b/src/astro/apparent-size.hpp index 13d1aae..b0285e2 100644 --- a/src/astro/apparent-size.hpp +++ b/src/astro/apparent-size.hpp @@ -20,6 +20,8 @@ #ifndef ANTKEEPER_ASTRO_APPARENT_SIZE_HPP #define ANTKEEPER_ASTRO_APPARENT_SIZE_HPP +#include + namespace astro { @@ -30,7 +32,11 @@ namespace astro * @param distance Distance to the celestial object. * @return Angular radius, in radians. */ -double find_angular_radius(double radius, double distance); +template +T angular_radius(T radius, T distance) +{ + return std::asin(radius / distance); +} } // namespace astro diff --git a/src/entity/components/blackbody.hpp b/src/entity/components/blackbody.hpp index 98ffcfc..24d608c 100644 --- a/src/entity/components/blackbody.hpp +++ b/src/entity/components/blackbody.hpp @@ -29,8 +29,8 @@ struct blackbody /// Effective temperature, in Kelvin. double temperature; - /// (Dependent) RGB luminous intensity, in candela. - double3 luminous_intensity; + /// (Dependent) RGB luminance, in lumens. + double3 luminance; }; } // namespace component diff --git a/src/entity/components/celestial-body.hpp b/src/entity/components/celestial-body.hpp index 025a1f0..fe5b056 100644 --- a/src/entity/components/celestial-body.hpp +++ b/src/entity/components/celestial-body.hpp @@ -41,8 +41,28 @@ struct celestial_body /// Location of the prime meridian at epoch, as a rotation about the north pole, in radians. double prime_meridian; + /* + /// Quadratic e coefficients for the right ascension of the body's north pole, in radians. Right ascension is calculated as `x + y * T + z * T^2`, where `T` is the Julian centuries (36525 days) from epoch. + double3 pole_ra; + + /// Quadratic coefficients for the declination of the body's north pole, in radians. Declination is calculated as `x + y * T + z * T^2`, where `T` is the Julian centuries (36525 days) from epoch. + double3 pole_ra; + + /// Quadratic coefficients for the rotation state of the body's prime meridian, in radians. Prime meridian rotation is calculated as `x + y * d + z * d^2`, where `d` is the days from epoch. + double3 prime_meridian; + + /// Linear coefficients of the nutation and precession angles, in radians. Angles are calculated as `x + y * d`, where `d` is the days from epoch. + std::vector nutation_precession_angles; + std::vector nutation_precession_ra; + std::vector nutation_precession_dec; + std::vector nutation_precession_pm; + */ + /// Sidereal rotation period, in rotations per day. double rotation_period; + + /// Geometric albedo + double albedo; }; } // namespace component diff --git a/src/entity/systems/astronomy.cpp b/src/entity/systems/astronomy.cpp index e64a830..402f6c5 100644 --- a/src/entity/systems/astronomy.cpp +++ b/src/entity/systems/astronomy.cpp @@ -21,6 +21,7 @@ #include "astro/apparent-size.hpp" #include "entity/components/blackbody.hpp" #include "entity/components/transform.hpp" +#include "entity/components/diffuse-reflector.hpp" #include "geom/intersection.hpp" #include "geom/cartesian.hpp" #include "color/color.hpp" @@ -31,6 +32,9 @@ #include "physics/light/refraction.hpp" #include "physics/atmosphere.hpp" #include "geom/cartesian.hpp" +#include "astro/apparent-size.hpp" +#include "astro/illuminance.hpp" +#include "geom/solid-angle.hpp" #include namespace entity { @@ -59,6 +63,8 @@ astronomy::astronomy(entity::registry& registry): observer_location{0, 0, 0}, sun_light(nullptr), sky_light(nullptr), + moon_light(nullptr), + camera(nullptr), sky_pass(nullptr) { // Construct transformation which transforms coordinates from ENU to EUS @@ -74,6 +80,8 @@ astronomy::astronomy(entity::registry& registry): void astronomy::update(double t, double dt) { + double total_illuminance = 0.0; + // Add scaled timestep to current time set_universal_time(universal_time + dt * time_scale); @@ -126,13 +134,13 @@ void astronomy::update(double t, double dt) // Update local transform if (orbit.parent != entt::null) { - transform.local.translation = math::normalize(math::type_cast(r_eus)) * 1000.0f; + transform.local.translation = math::normalize(math::type_cast(r_eus)); transform.local.rotation = math::type_cast(rotation_eus); - transform.local.scale = {50.0f, 50.0f, 50.0f}; + transform.local.scale = {1.0f, 1.0f, 1.0f}; } - /* - if (orbit.parent == entt::null) + + if (orbit.parent != entt::null) { // RA-DEC const double3 r_bci = icrf_to_bci * orbit.icrf_position; @@ -150,10 +158,10 @@ void astronomy::update(double t, double dt) const double el = math::degrees(r_enu_spherical.y); const double az = math::degrees(r_enu_spherical.z); - std::cout << "t: " << this->universal_time << "; ra: " << ra << "; dec: " << dec << std::endl; - std::cout << "t: " << this->universal_time << "; az: " << az << "; el: " << el << std::endl; + //std::cout << "t: " << this->universal_time << "; ra: " << ra << "; dec: " << dec << std::endl; + //std::cout << "t: " << this->universal_time << "; az: " << az << "; el: " << el << std::endl; } - */ + }); // Update blackbody lighting @@ -173,9 +181,6 @@ void astronomy::update(double t, double dt) // Calculate distance from observer to blackbody double blackbody_distance = math::length(blackbody_position_eus) - body.radius; - // Calculate blackbody distance attenuation - double distance_attenuation = 1.0 / (blackbody_distance * blackbody_distance); - // Init atmospheric transmittance double3 atmospheric_transmittance = {1.0, 1.0, 1.0}; @@ -221,23 +226,31 @@ void astronomy::update(double t, double dt) ) ); + // Calculate blackbody solid angle + const double angular_radius = astro::angular_radius(body.radius, blackbody_distance); + const double solid_angle = geom::solid_angle::cone(angular_radius); + + const double3 blackbody_illuminance = blackbody.luminance * solid_angle; + // Sun illuminance at the outer atmosphere - float3 sun_illuminance_outer = math::type_cast(blackbody.luminous_intensity * distance_attenuation); + float3 sun_illuminance_outer = math::type_cast(blackbody_illuminance); // Sun illuminance at sea level - float3 sun_illuminance_inner = math::type_cast(blackbody.luminous_intensity * distance_attenuation * atmospheric_transmittance); + float3 sun_illuminance_inner = math::type_cast(blackbody_illuminance * atmospheric_transmittance); // Update blackbody light color and intensity sun_light->set_color(sun_illuminance_inner); sun_light->set_intensity(1.0f); + total_illuminance += (sun_illuminance_inner.x + sun_illuminance_inner.y + sun_illuminance_inner.z) / 3.0; + // Upload blackbody params to sky pass if (this->sky_pass) { this->sky_pass->set_sun_position(math::type_cast(blackbody_position_eus)); this->sky_pass->set_sun_illuminance(sun_illuminance_outer, sun_illuminance_inner); - double blackbody_angular_radius = std::asin((body.radius * 2.0) / (blackbody_distance * 2.0)); + double blackbody_angular_radius = astro::angular_radius(body.radius, blackbody_distance); this->sky_pass->set_sun_angular_radius(static_cast(blackbody_angular_radius)); } } @@ -246,11 +259,109 @@ void astronomy::update(double t, double dt) { double3 blackbody_position_enu_spherical = physics::orbit::frame::enu::spherical(icrf_to_enu * orbit.icrf_position); - double illuminance = 25000.0 * std::max(0.0, std::sin(blackbody_position_enu_spherical.y)); + const double starlight_illuminance = 0.0011; + const double sky_illuminance = 25000.0 * std::max(0.0, std::sin(blackbody_position_enu_spherical.y)); + const double sky_light_illuminance = sky_illuminance + starlight_illuminance; + total_illuminance += sky_light_illuminance; sky_light->set_color({1.0f, 1.0f, 1.0f}); - sky_light->set_intensity(static_cast(illuminance)); + sky_light->set_intensity(static_cast(sky_illuminance)); } + + const double3& blackbody_icrf_position = orbit.icrf_position; + + // Update diffuse reflectors + this->registry.view().each( + [&](entity::id entity_id, const auto& reflector_body, const auto& reflector_orbit, const auto& reflector, const auto& transform) + { + // Calculate distance to blackbody and direction of incoming light + double3 blackbody_light_direction_icrf = reflector_orbit.icrf_position - blackbody_icrf_position; + double blackbody_distance = math::length(blackbody_light_direction_icrf); + blackbody_light_direction_icrf = blackbody_light_direction_icrf / blackbody_distance; + + // Transform blackbody light direction from the ICRF frame to the EUS frame + double3 blackbody_light_direction_eus = icrf_to_eus.r * blackbody_light_direction_icrf; + + // Calculate blackbody solid angle + const double blackbody_angular_radius = astro::angular_radius(body.radius, blackbody_distance); + const double blackbody_solid_angle = geom::solid_angle::cone(blackbody_angular_radius); + + // Calculate blackbody illuminance + + double3 view_direction_icrf = reflector_orbit.icrf_position - reference_orbit.icrf_position; + const double reflector_distance = math::length(view_direction_icrf); + view_direction_icrf = view_direction_icrf / reflector_distance; + + const double3 sunlight_illuminance = blackbody.luminance * blackbody_solid_angle; + const double reflector_angular_radius = astro::angular_radius(reflector_body.radius, reflector_distance); + const double reflector_solid_angle = geom::solid_angle::cone(reflector_angular_radius); + const double reflector_phase_factor = dot(view_direction_icrf, blackbody_light_direction_icrf) * 0.5 + 0.5; + + const double3 planet_luminance = (sunlight_illuminance * reference_body.albedo) / math::pi; + const double planet_angular_radius = astro::angular_radius(reference_body.radius, reflector_distance); + const double planet_solid_angle = geom::solid_angle::cone(planet_angular_radius); + const double planet_phase_factor = math::dot(-view_direction_icrf, math::normalize(reference_orbit.icrf_position - blackbody_icrf_position)) * 0.5 + 0.5; + const double3 planetlight_illuminance = planet_luminance * planet_solid_angle * planet_phase_factor; + double3 planetlight_direction_eus = math::normalize(icrf_to_eus.r * view_direction_icrf); + + const double3 reflected_sunlight_luminance = (sunlight_illuminance * reflector.albedo) / math::pi; + const double3 reflected_sunlight_illuminance = reflected_sunlight_luminance * reflector_solid_angle * reflector_phase_factor; + const double3 reflected_planetlight_luminance = (planetlight_illuminance * reflector.albedo) / math::pi; + const double3 reflected_planetlight_illuminance = reflected_planetlight_luminance * reflector_solid_angle; + + std::cout << "sunlight illuminance: " << sunlight_illuminance << std::endl; + std::cout << "reflected sunlight illuminance: " << reflected_sunlight_illuminance << std::endl; + std::cout << "planetlight illuminance: " << planetlight_illuminance << std::endl; + std::cout << "reflected planetlight illuminance: " << reflected_planetlight_illuminance << std::endl; + std::cout << "reflector phase: " << reflector_phase_factor << std::endl; + std::cout << "planet phase: " << planet_phase_factor << std::endl; + + if (this->sky_pass) + { + this->sky_pass->set_moon_position(transform.local.translation); + this->sky_pass->set_moon_rotation(transform.local.rotation); + this->sky_pass->set_moon_angular_radius(static_cast(reflector_angular_radius)); + this->sky_pass->set_moon_sunlight_direction(math::type_cast(blackbody_light_direction_eus)); + this->sky_pass->set_moon_sunlight_illuminance(math::type_cast(sunlight_illuminance)); + this->sky_pass->set_moon_planetlight_direction(math::type_cast(planetlight_direction_eus)); + this->sky_pass->set_moon_planetlight_illuminance(math::type_cast(planetlight_illuminance)); + } + + if (this->moon_light) + { + float3 reflector_up_eus = math::type_cast(icrf_to_eus.r * double3{0, 0, 1}); + + double3 reflected_illuminance = reflected_sunlight_illuminance + reflected_planetlight_illuminance; + reflected_illuminance *= std::max(0.0, std::sin(transform.local.translation.y)); + + total_illuminance += (reflected_illuminance.x + reflected_illuminance.y + reflected_illuminance.z) / 3.0; + + this->moon_light->set_color(math::type_cast(reflected_illuminance)); + this->moon_light->set_rotation + ( + math::look_rotation + ( + math::normalize(-transform.local.translation), + reflector_up_eus + ) + ); + } + + /* + std::cout << "moon: sun solid angle: " << blackbody_solid_angle << std::endl; + std::cout << "moon: sun illuminance: " << blackbody_illuminance << std::endl; + std::cout << "moon: moon luminance: " << reflector_luminance << std::endl; + std::cout << "sun brightness: " << sun_brightness << std::endl; + std::cout << "vega brightness: " << vega_brightness << std::endl; + std::cout << "earth: moon distance: " << reflector_distance << std::endl; + std::cout << "earth: moon solid angle: " << reflector_solid_angle << std::endl; + std::cout << "earth: moon phase: " << reflector_phase << std::endl; + std::cout << "earth: moon phase angle: " << math::degrees(reflector_phase_angle) << std::endl; + std::cout << "earth: moon illum %: " << reflector_illumination_factor * 100.0 << std::endl; + std::cout << "earth: moon illuminance: " << reflector_illuminance << std::endl; + std::cout << "earth: moon phase-modulated illuminance: " << reflector_illuminance * reflector_illumination_factor << std::endl; + */ + }); }); // Update sky pass topocentric frame @@ -280,6 +391,15 @@ void astronomy::update(double t, double dt) sky_pass->set_atmosphere_radii(reference_body.radius, reference_body.radius + reference_atmosphere.exosphere_altitude); } } + + // Auto-exposure + if (camera) + { + const double calibration = 250.0; + const double ev100 = std::log2((total_illuminance * 100.0) / calibration); + // std::cout << "EV100: " << ev100 << std::endl; + camera->set_exposure(static_cast(ev100)); + } } void astronomy::set_universal_time(double time) @@ -314,6 +434,16 @@ void astronomy::set_sky_light(scene::ambient_light* light) sky_light = light; } +void astronomy::set_moon_light(scene::directional_light* light) +{ + moon_light = light; +} + +void astronomy::set_camera(scene::camera* camera) +{ + this->camera = camera; +} + void astronomy::set_sky_pass(::render::sky_pass* pass) { this->sky_pass = pass; diff --git a/src/entity/systems/astronomy.hpp b/src/entity/systems/astronomy.hpp index 5cf7322..015c3a3 100644 --- a/src/entity/systems/astronomy.hpp +++ b/src/entity/systems/astronomy.hpp @@ -24,6 +24,7 @@ #include "entity/id.hpp" #include "scene/directional-light.hpp" #include "scene/ambient-light.hpp" +#include "scene/camera.hpp" #include "utility/fundamental-types.hpp" #include "math/se3.hpp" #include "render/passes/sky-pass.hpp" @@ -81,6 +82,8 @@ public: void set_sun_light(scene::directional_light* light); void set_sky_light(scene::ambient_light* light); + void set_moon_light(scene::directional_light* light); + void set_camera(scene::camera* camera); void set_sky_pass(::render::sky_pass* pass); @@ -105,6 +108,8 @@ private: scene::directional_light* sun_light; scene::ambient_light* sky_light; + scene::directional_light* moon_light; + scene::camera* camera; ::render::sky_pass* sky_pass; }; diff --git a/src/entity/systems/blackbody.cpp b/src/entity/systems/blackbody.cpp index 285427d..8cc01c0 100644 --- a/src/entity/systems/blackbody.cpp +++ b/src/entity/systems/blackbody.cpp @@ -51,7 +51,7 @@ void blackbody::set_rgb_wavelengths(const double3& wavelengths) rgb_wavelengths_m = wavelengths * 1e-9; } -void blackbody::update_luminous_intensity(entity::id entity_id) +void blackbody::update_luminance(entity::id entity_id) { // Abort if entity has no blackbody component if (!registry.has(entity_id)) @@ -60,8 +60,8 @@ void blackbody::update_luminous_intensity(entity::id entity_id) // Get blackbody component of the entity component::blackbody& blackbody = registry.get(entity_id); - // Clear luminous intensity - blackbody.luminous_intensity = {0, 0, 0}; + // Clear luminance + blackbody.luminance = {0, 0, 0}; // Abort if entity has no celestial body component if (!registry.has(entity_id)) @@ -70,47 +70,44 @@ void blackbody::update_luminous_intensity(entity::id entity_id) // Get celestial body component of the entity const component::celestial_body& celestial_body = registry.get(entity_id); - // Calculate (spherical) surface area of the celestial body - const double surface_area = 4.0 * math::pi * 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 + // Construct a lambda function which calculates the blackbody's RGB luminance of a given wavelength + auto rgb_luminance = [blackbody](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(blackbody.temperature, surface_area, wavelength_m); + const double spectral_radiance = physics::light::blackbody::spectral_radiance(blackbody.temperature, 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; + return spectral_color * spectral_radiance * 1e-9 * physics::light::max_luminous_efficacy; }; - // 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()); + // Integrate the blackbody RGB luminance over wavelengths in the visible spectrum + blackbody.luminance = math::quadrature::simpson(rgb_luminance, visible_wavelengths_nm.begin(), visible_wavelengths_nm.end()); } void blackbody::on_blackbody_construct(entity::registry& registry, entity::id entity_id, entity::component::blackbody& blackbody) { - update_luminous_intensity(entity_id); + update_luminance(entity_id); } void blackbody::on_blackbody_replace(entity::registry& registry, entity::id entity_id, entity::component::blackbody& blackbody) { - update_luminous_intensity(entity_id); + update_luminance(entity_id); } void blackbody::on_celestial_body_construct(entity::registry& registry, entity::id entity_id, entity::component::celestial_body& celestial_body) { - update_luminous_intensity(entity_id); + update_luminance(entity_id); } void blackbody::on_celestial_body_replace(entity::registry& registry, entity::id entity_id, entity::component::celestial_body& celestial_body) { - update_luminous_intensity(entity_id); + update_luminance(entity_id); } } // namespace system diff --git a/src/entity/systems/blackbody.hpp b/src/entity/systems/blackbody.hpp index 8439f9b..6c3fa3b 100644 --- a/src/entity/systems/blackbody.hpp +++ b/src/entity/systems/blackbody.hpp @@ -49,7 +49,7 @@ public: void set_rgb_wavelengths(const double3& wavelengths); private: - void update_luminous_intensity(entity::id entity_id); + void update_luminance(entity::id entity_id); void on_blackbody_construct(entity::registry& registry, entity::id entity_id, entity::component::blackbody& blackbody); void on_blackbody_replace(entity::registry& registry, entity::id entity_id, entity::component::blackbody& blackbody); diff --git a/src/game/state/boot.cpp b/src/game/state/boot.cpp index 1ddf52a..cc61f4e 100644 --- a/src/game/state/boot.cpp +++ b/src/game/state/boot.cpp @@ -492,6 +492,7 @@ void boot::setup_rendering() ctx.sky_pass = new render::sky_pass(ctx.rasterizer, ctx.hdr_framebuffer, ctx.resource_manager); ctx.sky_pass->set_enabled(false); + ctx.sky_pass->set_magnification(4.0f); ctx.app->get_event_dispatcher()->subscribe(ctx.sky_pass); ctx.ground_pass = new render::ground_pass(ctx.rasterizer, ctx.hdr_framebuffer, ctx.resource_manager); diff --git a/src/game/state/nuptial-flight.cpp b/src/game/state/nuptial-flight.cpp index c17551c..51c8a8d 100644 --- a/src/game/state/nuptial-flight.cpp +++ b/src/game/state/nuptial-flight.cpp @@ -43,6 +43,8 @@ #include "game/load.hpp" #include "game/ant/breed.hpp" #include "game/ant/morphogenesis.hpp" +#include "physics/time/time.hpp" +#include using namespace game::ant; @@ -93,11 +95,15 @@ nuptial_flight::nuptial_flight(game::context& ctx): { game::world::create_stars(ctx); game::world::create_sun(ctx); - game::world::create_planet(ctx); + game::world::create_em_bary(ctx); + game::world::create_earth(ctx); game::world::create_moon(ctx); - // Set time to solar noon - game::world::set_time(ctx, 50.0);//107.3); + // Set world time + const double utc = 0.0; + const double equinox_2000 = physics::time::gregorian::to_ut1(2000, 1, 1, 12, 0, 0.0, utc); + const double spring_2000 = physics::time::gregorian::to_ut1(2000, 6, 19, 12, 0, 0.0, utc); + game::world::set_time(ctx, 55.0); // Freeze time game::world::set_time_scale(ctx, 0.0); @@ -277,9 +283,10 @@ void nuptial_flight::setup_camera() constraint_stack.head = spring_constraint_eid; ctx.entity_registry->assign(camera_eid, constraint_stack); } + + ctx.surface_camera->set_exposure(15.0f); - float ev100 = 15.0f; - ctx.surface_camera->set_exposure(ev100); + ctx.astronomy_system->set_camera(ctx.surface_camera); } void nuptial_flight::enable_keeper_controls() @@ -306,7 +313,7 @@ void nuptial_flight::enable_keeper_controls() bool gamepad_invert_pan = false; bool mouse_look_toggle = false; ctx.mouse_look = false; - const double time_scale = 5000.0; + const double time_scale = 10000.0; if (ctx.config->contains("mouse_tilt_sensitivity")) mouse_tilt_sensitivity = math::radians((*ctx.config)["mouse_tilt_sensitivity"].get()); @@ -665,7 +672,7 @@ void nuptial_flight::enable_ant_controls() float gamepad_pan_sensitivity = 1.0f; bool gamepad_invert_tilt = false; bool gamepad_invert_pan = false; - const double time_scale = 5000.0; + const double time_scale = 10000.0; if (ctx.config->contains("mouse_tilt_sensitivity")) mouse_tilt_sensitivity = math::radians((*ctx.config)["mouse_tilt_sensitivity"].get()); diff --git a/src/game/world.cpp b/src/game/world.cpp index 0fc1610..53299b6 100644 --- a/src/game/world.cpp +++ b/src/game/world.cpp @@ -50,6 +50,7 @@ #include "gl/texture-filter.hpp" #include "render/material-flags.hpp" #include "config.hpp" +#include namespace game { namespace world { @@ -68,6 +69,8 @@ void create_stars(game::context& ctx) float* star_vertex_data = new float[star_count * star_vertex_size]; float* star_vertex = star_vertex_data; + double starlight_illuminance = 0.0; + // Build star catalog vertex data for (std::size_t i = 1; i < star_catalog->size(); ++i) { @@ -91,6 +94,8 @@ void create_stars(game::context& ctx) continue; } + starlight_illuminance += astro::vmag_to_lux(vmag); + // Convert right ascension and declination from degrees to radians ra = math::wrap_radians(math::radians(ra)); dec = math::wrap_radians(math::radians(dec)); @@ -123,6 +128,8 @@ void create_stars(game::context& ctx) *(star_vertex++) = static_cast(brightness); } + std::cout << "TOTAL STARLIGHT: " << starlight_illuminance << std::endl; + // Unload star catalog ctx.resource_manager->unload("stars.csv"); @@ -203,17 +210,28 @@ void create_sun(game::context& ctx) ctx.astronomy_system->set_sky_light(sky_light); } -void create_planet(game::context& ctx) +void create_em_bary(game::context& ctx) { - // Create planet entity - entity::archetype* planet_archetype = ctx.resource_manager->load("planet.ent"); - entity::id planet_eid = planet_archetype->create(*ctx.entity_registry); - ctx.entities["planet"] = planet_eid; + // Create earth-moon barycenter entity + entity::archetype* em_bary_archetype = ctx.resource_manager->load("em-bary.ent"); + entity::id em_bary_eid = em_bary_archetype->create(*ctx.entity_registry); + ctx.entities["em_bary"] = em_bary_eid; // Assign orbital parent - ctx.entity_registry->get(planet_eid).parent = ctx.entities["sun"]; + ctx.entity_registry->get(em_bary_eid).parent = ctx.entities["sun"]; +} + +void create_earth(game::context& ctx) +{ + // Create earth entity + entity::archetype* earth_archetype = ctx.resource_manager->load("earth.ent"); + entity::id earth_eid = earth_archetype->create(*ctx.entity_registry); + ctx.entities["earth"] = earth_eid; - // Assign planetary terrain component + // Assign orbital parent + ctx.entity_registry->get(earth_eid).parent = ctx.entities["em_bary"]; + + // Assign earth terrain component entity::component::terrain terrain; terrain.elevation = [](double, double) -> double { @@ -222,10 +240,10 @@ void create_planet(game::context& ctx) }; terrain.max_lod = 0; terrain.patch_material = nullptr; - //ctx.entity_registry->assign(planet_eid, terrain); + //ctx.entity_registry->assign(earth_eid, terrain); - // Pass planet to astronomy system as reference body - ctx.astronomy_system->set_reference_body(planet_eid); + // Pass earth to astronomy system as reference body + ctx.astronomy_system->set_reference_body(earth_eid); } void create_moon(game::context& ctx) @@ -236,10 +254,22 @@ void create_moon(game::context& ctx) ctx.entities["moon"] = moon_eid; // Assign orbital parent - ctx.entity_registry->get(moon_eid).parent = ctx.entities["planet"]; + ctx.entity_registry->get(moon_eid).parent = ctx.entities["em_bary"]; // Pass moon model to sky pass ctx.sky_pass->set_moon_model(ctx.resource_manager->load("moon.mdl")); + + // Create moon directional light scene object + scene::directional_light* moon_light = new scene::directional_light(); + moon_light->set_color({1, 1, 1}); + moon_light->set_intensity(1.0f); + moon_light->update_tweens(); + + // Add moon light scene objects to surface scene + ctx.surface_scene->add_object(moon_light); + + // Pass moon light scene object to astronomy system + ctx.astronomy_system->set_moon_light(moon_light); } void set_time(game::context& ctx, double t) diff --git a/src/game/world.hpp b/src/game/world.hpp index 1ecf4f3..68d4c4f 100644 --- a/src/game/world.hpp +++ b/src/game/world.hpp @@ -33,8 +33,11 @@ void create_stars(game::context& ctx); /// Creates the sun. void create_sun(game::context& ctx); -/// Creates the planet. -void create_planet(game::context& ctx); +/// Creates the earth-moon barycenter. +void create_em_bary(game::context& ctx); + +/// Creates the earth. +void create_earth(game::context& ctx); /// Creates the moon. void create_moon(game::context& ctx); diff --git a/src/geom/solid-angle.hpp b/src/geom/solid-angle.hpp new file mode 100644 index 0000000..ac7cf3b --- /dev/null +++ b/src/geom/solid-angle.hpp @@ -0,0 +1,47 @@ +/* + * 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 . + */ + +#ifndef ANTKEEPER_GEOM_SOLID_ANGLE_HPP +#define ANTKEEPER_GEOM_SOLID_ANGLE_HPP + +#include "math/constants.hpp" +#include + +namespace geom { + +/// Solid angle functions. +namespace solid_angle { + +/** + * Calculates the solid angle of a cone. + * + * @param theta Apex angle of the cone, in radians. + * + * @return Solid angle of the cone, in steradians. + */ +template +T cone(T theta) +{ + return math::two_pi * (T(1) - std::cos(theta)); +} + +} // namespace solid_angle +} // namespace geom + +#endif // ANTKEEPER_GEOM_SOLID_ANGLE_HPP diff --git a/src/physics/light/blackbody.hpp b/src/physics/light/blackbody.hpp index 0061ec3..4864973 100644 --- a/src/physics/light/blackbody.hpp +++ b/src/physics/light/blackbody.hpp @@ -57,10 +57,11 @@ T radiant_flux(T t, T a); * * @param t Temperature of the blackbody, in kelvin. * @param a Surface area of the blackbody, in square meters. + * @param omega Solid angle, in steradians. * @return Radiant intensity of the blackbody, in watt per steradian. */ template -T radiant_intensity(T t, T a); +T radiant_intensity(T t, T a, T omega); /** * Calculates the spectral exitance of a blackbody for the given wavelength. @@ -91,11 +92,12 @@ T spectral_flux(T t, T a, T lambda, T c = constants::speed_of_light); * @param t Temperature of the blackbody, in kelvin. * @param a Surface area of the blackbody, in square meters. * @param lambda Wavelength of light, in meters. + * @param omega Solid angle, in steradians. * @param c Speed of light in medium. * @return Spectral intensity of the blackbody for the given wavelength, in watt per steradian per meter. */ template -T spectral_intensity(T t, T a, T lambda, T c = constants::speed_of_light); +T spectral_intensity(T t, T a, T lambda, T omega, T c = constants::speed_of_light); /** * Calculates the spectral radiance of a blackbody for the given wavelength. @@ -122,9 +124,9 @@ T radiant_flux(T t, T a) } template -T radiant_intensity(T t, T a) +T radiant_intensity(T t, T a, T omega) { - return radiant_flux(t, a) / (T(4) * math::pi); + return radiant_flux(t, a) / omega; } template @@ -149,9 +151,9 @@ T spectral_flux(T t, T a, T lambda, T c) } template -T spectral_intensity(T t, T a, T lambda, T c) +T spectral_intensity(T t, T a, T lambda, T omega, T c) { - return spectral_flux(t, a, lambda, c) / (T(4) * math::pi); + return spectral_flux(t, a, lambda, c) / omega; } template diff --git a/src/physics/orbit/frame.hpp b/src/physics/orbit/frame.hpp index 8293694..82adad4 100644 --- a/src/physics/orbit/frame.hpp +++ b/src/physics/orbit/frame.hpp @@ -211,9 +211,9 @@ namespace bci { { const math::quaternion r = math::normalize ( - math::quaternion::rotate_z(-w) * + math::quaternion::rotate_z(-math::half_pi - ra) * math::quaternion::rotate_x(-math::half_pi + dec) * - math::quaternion::rotate_z(-math::half_pi - ra) + math::quaternion::rotate_z(-w) ); return math::transformation::se3{{T(0), T(0), T(0)}, r}; @@ -296,9 +296,10 @@ namespace bcbf { { const math::quaternion r = math::normalize ( - math::quaternion::rotate_z(math::half_pi + ra) * + math::quaternion::rotate_z(w) * math::quaternion::rotate_x(math::half_pi - dec) * - math::quaternion::rotate_z(w) + math::quaternion::rotate_z(math::half_pi + ra) + ); return math::transformation::se3{{T(0), T(0), T(0)}, r}; diff --git a/src/physics/time/gregorian.hpp b/src/physics/time/gregorian.hpp new file mode 100644 index 0000000..01d861b --- /dev/null +++ b/src/physics/time/gregorian.hpp @@ -0,0 +1,77 @@ +/* + * 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 . + */ + +#ifndef ANTKEEPER_PHYSICS_TIME_GREGORIAN_HPP +#define ANTKEEPER_PHYSICS_TIME_GREGORIAN_HPP + +#include "physics/time/jd.hpp" + +namespace physics { +namespace time { + +// Gregorian calender time. +namespace gregorian { + +/** + * Calculates the JD time from a Gregorian date and time. Valid for all dates after November 23, −4713. + * + * @param year Astronomical year numbering. 1 BC is `0`, 2 BC is `-1`. + * @param month Month number on `[1, 12]`. + * @param day Day number on `[1, 31]`. + * @param hour Hour number on `[0, 23]`. + * @param minute Minute number on `[0, 59]`. + * @param second Fractional second on `[0.0, 60.0)`. + * @param utc UTC offset. + * + * @return JD time. + * + * @see L. E. Doggett, Ch. 12, "Calendars", p. 606, in Seidelmann 1992 + */ +template +T to_jd(int year, int month, int day, int hour, int minute, T second, T utc) +{ + T jdn = static_cast((1461 * (year + 4800 + (month - 14) / 12)) / 4 + (367 * (month - 2 - 12 * ((month - 14) / 12))) / 12 - (3 * ((year + 4900 + (month - 14) / 12) / 100)) / 4 + day - 32075); + return jdn + static_cast(hour - 12) / T(24) + static_cast(minute) / T(1440) + static_cast(second) / T(86400) + utc / T(24); +} + +/** + * Calculates the UT1 time from a Gregorian date and time. Valid for all dates after November 23, −4713. + * + * @param year Astronomical year numbering. 1 BC is `0`, 2 BC is `-1`. + * @param month Month number on `[1, 12]`. + * @param day Day number on `[1, 31]`. + * @param hour Hour number on `[0, 23]`. + * @param minute Minute number on `[0, 59]`. + * @param second Fractional second on `[0.0, 60.0)`. + * @param utc UTC offset. + * + * @return UT1 time. + */ +template +T to_ut1(int year, int month, int day, int hour, int minute, T second, T utc) +{ + return physics::time::jd::to_ut1(to_jd(year, month, day, hour, minute, second, utc)); +} + +} // namespace gregorian + +} // namespace time +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_TIME_GREGORIAN_HPP diff --git a/src/astro/apparent-size.cpp b/src/physics/time/jd.hpp similarity index 67% rename from src/astro/apparent-size.cpp rename to src/physics/time/jd.hpp index 159986c..43bc8ee 100644 --- a/src/astro/apparent-size.cpp +++ b/src/physics/time/jd.hpp @@ -17,15 +17,30 @@ * along with Antkeeper source code. If not, see . */ -#include "apparent-size.hpp" -#include +#ifndef ANTKEEPER_PHYSICS_TIME_JD_HPP +#define ANTKEEPER_PHYSICS_TIME_JD_HPP -namespace astro -{ +namespace physics { +namespace time { + +// Julian day (JD) +namespace jd { -double find_angular_radius(double radius, double distance) +/** + * Converts JD to UT1 + * + * @param t JD time. + * @return UT1 time. + */ +template +T to_ut1(T t) { - return std::asin(radius / distance); + return t - T(2451545); } -} // namespace astro +} // namespace jd + +} // namespace time +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_TIME_JD_HPP diff --git a/src/physics/time/time.hpp b/src/physics/time/time.hpp index ec2f6ca..2b60160 100644 --- a/src/physics/time/time.hpp +++ b/src/physics/time/time.hpp @@ -27,6 +27,8 @@ namespace time {} } // namespace physics +#include "gregorian.hpp" +#include "jd.hpp" #include "ut1.hpp" #endif // ANTKEEPER_PHYSICS_TIME_HPP diff --git a/src/physics/time/ut1.hpp b/src/physics/time/ut1.hpp index e91560d..6879aa5 100644 --- a/src/physics/time/ut1.hpp +++ b/src/physics/time/ut1.hpp @@ -25,18 +25,27 @@ namespace physics { namespace time { -/// Functions which operate on UT1 universal time. +/// UT1 universal time. namespace ut1 { +/** + * Converts UT1 to JD + * + * @param t UT1 time. + * @return JD time. + */ +template +T to_jd(T t) +{ + return t + T(2451545); +} + /** * Calculates the Earth Rotation Angle (ERA) at a given UT1 date. * * @param t J2000 UT1 date. * @return ERA at the given date, in radians. */ -template -T era(T t); - template T era(T t) { diff --git a/src/render/passes/ground-pass.cpp b/src/render/passes/ground-pass.cpp index 8b41326..f49b706 100644 --- a/src/render/passes/ground-pass.cpp +++ b/src/render/passes/ground-pass.cpp @@ -123,7 +123,11 @@ void ground_pass::render(const render::context& ctx, render::queue& queue) const const scene::directional_light* directional_light = static_cast(light); // Pre-expose light - directional_light_color = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; + float3 light_color = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; + if (light_color.x < directional_light_color.x) + break; + + directional_light_color = light_color; directional_light_direction = static_cast(light)->get_direction_tween().interpolate(ctx.alpha); break; diff --git a/src/render/passes/sky-pass.cpp b/src/render/passes/sky-pass.cpp index 0333573..3663656 100644 --- a/src/render/passes/sky-pass.cpp +++ b/src/render/passes/sky-pass.cpp @@ -74,7 +74,15 @@ sky_pass::sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffe sun_illuminance_outer_tween(float3{1.0f, 1.0f, 1.0f}, math::lerp), sun_illuminance_inner_tween(float3{1.0f, 1.0f, 1.0f}, math::lerp), icrf_to_eus_translation({0, 0, 0}, math::lerp), - icrf_to_eus_rotation(math::quaternion::identity(), math::nlerp) + icrf_to_eus_rotation(math::quaternion::identity(), math::nlerp), + moon_position_tween(float3{0, 0, 0}, math::lerp), + moon_rotation_tween(math::quaternion::identity(), math::nlerp), + moon_angular_radius_tween(0.0f, math::lerp), + moon_sunlight_direction_tween(float3{0, 0, 0}, math::lerp), + moon_sunlight_illuminance_tween(float3{0, 0, 0}, math::lerp), + moon_planetlight_direction_tween(float3{0, 0, 0}, math::lerp), + moon_planetlight_illuminance_tween(float3{0, 0, 0}, math::lerp), + magnification(1.0f) {} sky_pass::~sky_pass() @@ -146,8 +154,8 @@ void sky_pass::render(const render::context& ctx, render::queue& queue) const if (sun_direction_input) sun_direction_input->upload(sun_direction); if (sun_angular_radius_input) - sun_angular_radius_input->upload(sun_angular_radius); - + sun_angular_radius_input->upload(sun_angular_radius * magnification); + // Pre-exposure sun color if (sun_illuminance_input) sun_illuminance_input->upload(sun_illuminance_outer * ctx.exposure); @@ -218,38 +226,43 @@ void sky_pass::render(const render::context& ctx, render::queue& queue) const rasterizer->draw_arrays(*stars_model_vao, stars_model_drawing_mode, stars_model_start_index, stars_model_index_count); } - /* // Draw moon model float3 moon_position = moon_position_tween.interpolate(ctx.alpha); - float moon_angular_radius = math::radians(2.0f); - if (moon_position.y >= -moon_angular_radius) + float moon_angular_radius = moon_angular_radius_tween.interpolate(ctx.alpha) * magnification; + //if (moon_position.y >= -moon_angular_radius) { float moon_distance = (clip_near + clip_far) * 0.5f; float moon_radius = moon_angular_radius * moon_distance; math::transform moon_transform; - moon_transform.translation = moon_position * -moon_distance; - moon_transform.rotation = math::quaternion::identity(); + moon_transform.translation = math::normalize(moon_position) * moon_distance; + moon_transform.rotation = moon_rotation_tween.interpolate(ctx.alpha); moon_transform.scale = {moon_radius, moon_radius, moon_radius}; model = math::matrix_cast(moon_transform); - model_view = view * model; - model_view_projection = projection * model_view; float3x3 normal_model = math::transpose(math::inverse(math::resize<3, 3>(model))); rasterizer->use_program(*moon_shader_program); - if (moon_model_view_projection_input) - moon_model_view_projection_input->upload(model_view_projection); + if (moon_model_input) + moon_model_input->upload(model); + if (moon_view_projection_input) + moon_view_projection_input->upload(view_projection); if (moon_normal_model_input) moon_normal_model_input->upload(normal_model); - if (moon_moon_position_input) - moon_moon_position_input->upload(moon_position); - if (moon_sun_position_input) - moon_sun_position_input->upload(sun_position); + if (moon_camera_position_input) + moon_camera_position_input->upload(ctx.camera_transform.translation); + if (moon_sunlight_direction_input) + moon_sunlight_direction_input->upload(math::normalize(moon_sunlight_direction_tween.interpolate(ctx.alpha))); + if (moon_planetlight_direction_input) + moon_planetlight_direction_input->upload(math::normalize(moon_planetlight_direction_tween.interpolate(ctx.alpha))); + if (moon_sunlight_illuminance_input) + moon_sunlight_illuminance_input->upload(moon_sunlight_illuminance_tween.interpolate(ctx.alpha) * ctx.exposure); + if (moon_planetlight_illuminance_input) + moon_planetlight_illuminance_input->upload(moon_planetlight_illuminance_tween.interpolate(ctx.alpha) * ctx.exposure); + moon_material->upload(ctx.alpha); rasterizer->draw_arrays(*moon_model_vao, moon_model_drawing_mode, moon_model_start_index, moon_model_index_count); } - */ } void sky_pass::set_sky_model(const model* model) @@ -322,10 +335,14 @@ void sky_pass::set_moon_model(const model* model) if (moon_shader_program) { - moon_model_view_projection_input = moon_shader_program->get_input("model_view_projection"); + moon_model_input = moon_shader_program->get_input("model"); + moon_view_projection_input = moon_shader_program->get_input("view_projection"); moon_normal_model_input = moon_shader_program->get_input("normal_model"); - moon_moon_position_input = moon_shader_program->get_input("moon_position"); - moon_sun_position_input = moon_shader_program->get_input("sun_position"); + moon_camera_position_input = moon_shader_program->get_input("camera_position"); + moon_sunlight_direction_input = moon_shader_program->get_input("sunlight_direction"); + moon_sunlight_illuminance_input = moon_shader_program->get_input("sunlight_illuminance"); + moon_planetlight_direction_input = moon_shader_program->get_input("planetlight_direction"); + moon_planetlight_illuminance_input = moon_shader_program->get_input("planetlight_illuminance"); } } } @@ -416,7 +433,19 @@ void sky_pass::update_tweens() sun_illuminance_inner_tween.update(); icrf_to_eus_translation.update(); icrf_to_eus_rotation.update(); + moon_position_tween.update(); + moon_rotation_tween.update(); + moon_angular_radius_tween.update(); + moon_sunlight_direction_tween.update(); + moon_sunlight_illuminance_tween.update(); + moon_planetlight_direction_tween.update(); + moon_planetlight_illuminance_tween.update(); +} + +void sky_pass::set_magnification(float magnification) +{ + this->magnification = magnification; } void sky_pass::set_icrf_to_eus(const math::transformation::se3& transformation) @@ -474,6 +503,36 @@ void sky_pass::set_moon_position(const float3& position) moon_position_tween[1] = position; } +void sky_pass::set_moon_rotation(const math::quaternion& rotation) +{ + moon_rotation_tween[1] = rotation; +} + +void sky_pass::set_moon_angular_radius(float angular_radius) +{ + moon_angular_radius_tween[1] = angular_radius; +} + +void sky_pass::set_moon_sunlight_direction(const float3& direction) +{ + moon_sunlight_direction_tween[1] = direction; +} + +void sky_pass::set_moon_sunlight_illuminance(const float3& illuminance) +{ + moon_sunlight_illuminance_tween[1] = illuminance; +} + +void sky_pass::set_moon_planetlight_direction(const float3& direction) +{ + moon_planetlight_direction_tween[1] = direction; +} + +void sky_pass::set_moon_planetlight_illuminance(const float3& illuminance) +{ + moon_planetlight_illuminance_tween[1] = illuminance; +} + void sky_pass::handle_event(const mouse_moved_event& event) { mouse_position = {static_cast(event.x), static_cast(event.y)}; diff --git a/src/render/passes/sky-pass.hpp b/src/render/passes/sky-pass.hpp index 723c549..8072087 100644 --- a/src/render/passes/sky-pass.hpp +++ b/src/render/passes/sky-pass.hpp @@ -55,6 +55,8 @@ public: void update_tweens(); + void set_magnification(float scale); + void set_sky_model(const model* model); void set_moon_model(const model* model); void set_stars_model(const model* model); @@ -72,6 +74,12 @@ public: void set_atmosphere_radii(float inner, float outer); void set_moon_position(const float3& position); + void set_moon_rotation(const math::quaternion& rotation); + void set_moon_angular_radius(float angular_radius); + void set_moon_sunlight_direction(const float3& direction); + void set_moon_sunlight_illuminance(const float3& illuminance); + void set_moon_planetlight_direction(const float3& direction); + void set_moon_planetlight_illuminance(const float3& illuminance); private: virtual void handle_event(const mouse_moved_event& event); @@ -93,10 +101,14 @@ private: const gl::shader_input* atmosphere_radii_input; gl::shader_program* moon_shader_program; - const gl::shader_input* moon_model_view_projection_input; + const gl::shader_input* moon_model_input; + const gl::shader_input* moon_view_projection_input; const gl::shader_input* moon_normal_model_input; - const gl::shader_input* moon_moon_position_input; - const gl::shader_input* moon_sun_position_input; + const gl::shader_input* moon_camera_position_input; + const gl::shader_input* moon_sunlight_direction_input; + const gl::shader_input* moon_sunlight_illuminance_input; + const gl::shader_input* moon_planetlight_direction_input; + const gl::shader_input* moon_planetlight_illuminance_input; const model* sky_model; const material* sky_material; @@ -151,7 +163,12 @@ private: tween> icrf_to_eus_rotation; tween moon_position_tween; - + tween> moon_rotation_tween; + tween moon_angular_radius_tween; + tween moon_sunlight_direction_tween; + tween moon_sunlight_illuminance_tween; + tween moon_planetlight_direction_tween; + tween moon_planetlight_illuminance_tween; float sun_angular_radius; float2 scale_height_rm; @@ -159,6 +176,8 @@ private: float3 mie_scattering; float2 mie_anisotropy; float3 atmosphere_radii; + + float magnification; }; } // namespace render diff --git a/src/resources/entity-archetype-loader.cpp b/src/resources/entity-archetype-loader.cpp index 55dfa80..f5bb4f0 100644 --- a/src/resources/entity-archetype-loader.cpp +++ b/src/resources/entity-archetype-loader.cpp @@ -23,6 +23,7 @@ #include "entity/components/atmosphere.hpp" #include "entity/components/behavior.hpp" #include "entity/components/collision.hpp" +#include "entity/components/diffuse-reflector.hpp" #include "entity/components/terrain.hpp" #include "entity/components/transform.hpp" #include "entity/components/model.hpp" @@ -103,6 +104,7 @@ static bool load_component_celestial_body(entity::archetype& archetype, const js component.pole_dec = 0.0; component.prime_meridian = 0.0; component.rotation_period = 0.0; + component.albedo = 0.0; if (element.contains("radius")) component.radius = element["radius"].get(); @@ -116,6 +118,8 @@ static bool load_component_celestial_body(entity::archetype& archetype, const js component.prime_meridian = math::radians(element["prime_meridian"].get()); if (element.contains("rotation_period")) component.rotation_period = element["rotation_period"].get(); + if (element.contains("albedo")) + component.albedo = element["albedo"].get(); archetype.set(component); @@ -137,6 +141,19 @@ static bool load_component_collision(entity::archetype& archetype, resource_mana return (component.mesh != nullptr); } +static bool load_component_diffuse_reflector(entity::archetype& archetype, const json& element) +{ + entity::component::diffuse_reflector component; + component.albedo = 0.0; + + if (element.contains("albedo")) + component.albedo = element["albedo"].get(); + + archetype.set(component); + + return true; +} + static bool load_component_model(entity::archetype& archetype, resource_manager& resource_manager, const json& element) { entity::component::model component; @@ -236,6 +253,8 @@ static bool load_component(entity::archetype& archetype, resource_manager& resou return load_component_celestial_body(archetype, element.value()); if (element.key() == "collision") return load_component_collision(archetype, resource_manager, element.value()); + if (element.key() == "diffuse_reflector") + return load_component_diffuse_reflector(archetype, element.value()); if (element.key() == "model") return load_component_model(archetype, resource_manager, element.value()); if (element.key() == "orbit")