From a402e70a90c1ec1c9643c412056cfd729ccedd6e Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Sat, 24 Sep 2022 16:41:09 +0800 Subject: [PATCH] Improve world creation functions with added exception handling, add colors to logger on Windows --- src/debug/logger.cpp | 41 +++- src/game/context.hpp | 4 + src/game/load.cpp | 4 +- src/game/state/nuptial-flight.cpp | 27 +-- src/game/world.cpp | 345 ++++++++++++++++++++++-------- src/game/world.hpp | 54 +++-- src/physics/time/constants.hpp | 33 +++ src/physics/time/gregorian.hpp | 2 +- src/physics/time/time.hpp | 1 + 9 files changed, 382 insertions(+), 129 deletions(-) create mode 100644 src/physics/time/constants.hpp diff --git a/src/debug/logger.cpp b/src/debug/logger.cpp index 1aca8b3..de5fa80 100644 --- a/src/debug/logger.cpp +++ b/src/debug/logger.cpp @@ -21,6 +21,11 @@ #include "utility/timestamp.hpp" #include +#if defined(_WIN32) + #define WIN32_LEAN_AND_MEAN + #include +#endif + namespace debug { logger::logger(): @@ -84,17 +89,45 @@ void logger::log(const std::string& text) void logger::warning(const std::string& text) { + #if defined(_WIN32) + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN); + #endif + log(warning_prefix + text + warning_postfix); + + #if defined(_WIN32) + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + #endif } void logger::error(const std::string& text) { + #if defined(_WIN32) + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED); + #endif + log(error_prefix + text + error_postfix); + + #if defined(_WIN32) + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + #endif } void logger::success(const std::string& text) { + /* + #if defined(_WIN32) + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN); + #endif + */ + log(success_prefix + text + success_postfix); + + /* + #if defined(_WIN32) + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + #endif + */ } void logger::set_auto_newline(bool enabled) @@ -170,21 +203,21 @@ void logger::pop_task(int status, std::string error) return; } - std::string message = "} "; + std::string message = "} => "; tasks.pop(); if (status == EXIT_SUCCESS) { - message += "=> success"; + message += "success"; if (!auto_newline) message += "\n"; - log(message); + success(message); } else { - message += "failed"; + message += "failure"; if (!error.empty()) message += " (" + error + ")"; if (!auto_newline) diff --git a/src/game/context.hpp b/src/game/context.hpp index f3db354..91c572c 100644 --- a/src/game/context.hpp +++ b/src/game/context.hpp @@ -294,6 +294,10 @@ struct context entity::system::astronomy* astronomy_system; entity::system::orbit* orbit_system; entity::system::proteome* proteome_system; + + double elevation; + double latitude; + double longitude; }; } // namespace game diff --git a/src/game/load.cpp b/src/game/load.cpp index 525dbf4..c5d6000 100644 --- a/src/game/load.cpp +++ b/src/game/load.cpp @@ -18,6 +18,7 @@ */ #include "game/load.hpp" +#include "game/world.hpp" #include "application.hpp" #include "debug/logger.hpp" #include "resources/json.hpp" @@ -61,7 +62,8 @@ void biome(game::context& ctx, const std::filesystem::path& path) else ctx.logger->warning("Biome longitude undefined"); - ctx.astronomy_system->set_observer_location({elevation, latitude, longitude}); + // Set location + game::world::set_location(ctx, elevation, latitude, longitude); } else { diff --git a/src/game/state/nuptial-flight.cpp b/src/game/state/nuptial-flight.cpp index d12f782..34975ab 100644 --- a/src/game/state/nuptial-flight.cpp +++ b/src/game/state/nuptial-flight.cpp @@ -44,7 +44,6 @@ #include "game/load.hpp" #include "game/ant/breed.hpp" #include "game/ant/morphogenesis.hpp" -#include "physics/time/time.hpp" #include "math/interpolation.hpp" #include @@ -75,7 +74,7 @@ nuptial_flight::nuptial_flight(game::context& ctx): breed.sculpturing = ctx.resource_manager->load("politus-sculpturing.dna"); breed.pigmentation = ctx.resource_manager->load("rust-pigmentation.dna"); breed.egg = ctx.resource_manager->load("ellipsoid-egg.dna"); - breed.larva = ctx.resource_manager->load("old-larva.dna"); + breed.larva = ctx.resource_manager->load("long-neck-larva.dna"); breed.cocoon = ctx.resource_manager->load("cocoon-present.dna"); breed.pilosity = ctx.resource_manager->load("hairless-pilosity.dna"); breed.forewings = nullptr; @@ -94,23 +93,9 @@ nuptial_flight::nuptial_flight(game::context& ctx): // Create world if not yet created if (ctx.entities.find("planet") == ctx.entities.end()) - { + { // Create cosmos - game::world::load_ephemeris(ctx); - game::world::create_stars(ctx); - game::world::create_sun(ctx); - game::world::create_em_bary(ctx); - game::world::create_earth(ctx); - game::world::create_moon(ctx); - - // 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 summer_2022 = physics::time::gregorian::to_ut1(2022, 6, 21, 12, 0, 0.0, utc); - game::world::set_time(ctx, summer_2022); - - // Set time scale - game::world::set_time_scale(ctx, 60.0); + game::world::cosmogenesis(ctx); // Create boids for (int i = 0; i < 50; ++i) @@ -160,6 +145,12 @@ nuptial_flight::nuptial_flight(game::context& ctx): // Load biome game::load::biome(ctx, "desert-scrub.bio"); + // Set world time + game::world::set_time(ctx, 2022, 6, 21, 12, 0, 0.0); + + // Set world time scale + game::world::set_time_scale(ctx, 60.0); + // Setup and enable sky and ground passes ctx.sky_pass->set_enabled(true); ctx.ground_pass->set_enabled(true); diff --git a/src/game/world.cpp b/src/game/world.cpp index 0bfc9a2..604beba 100644 --- a/src/game/world.cpp +++ b/src/game/world.cpp @@ -39,6 +39,8 @@ #include "physics/light/photometry.hpp" #include "physics/orbit/orbit.hpp" #include "physics/orbit/ephemeris.hpp" +#include "physics/time/gregorian.hpp" +#include "physics/time/constants.hpp" #include "render/material.hpp" #include "render/model.hpp" #include "render/passes/shadow-map-pass.hpp" @@ -55,16 +57,145 @@ namespace game { namespace world { +/// Loads an ephemeris. +static void load_ephemeris(game::context& ctx); + +/// Creates the fixed stars. +static void create_stars(game::context& ctx); + +/// Creates the Sun. +static void create_sun(game::context& ctx); + +/// Creates the Earth-Moon system. +static void create_earth_moon_system(game::context& ctx); + +/// Creates the Earth. +static void create_earth(game::context& ctx); + +/// Creates the Moon. +static void create_moon(game::context& ctx); + +void cosmogenesis(game::context& ctx) +{ + ctx.logger->push_task("Generating cosmos"); + + load_ephemeris(ctx); + create_stars(ctx); + create_sun(ctx); + create_earth_moon_system(ctx); + + ctx.logger->pop_task(EXIT_SUCCESS); +} + +void set_location(game::context& ctx, double elevation, double latitude, double longitude) +{ + // Update context location + ctx.elevation = elevation; + ctx.latitude = latitude; + ctx.longitude = longitude; + + // Pass location to astronomy system + ctx.astronomy_system->set_observer_location({elevation, latitude, longitude}); +} + +void set_time(game::context& ctx, double t) +{ + ctx.logger->push_task("Setting time to UT1 " + std::to_string(t)); + try + { + ctx.astronomy_system->set_time(t); + ctx.orbit_system->set_time(t); + } + catch (const std::exception&) + { + ctx.logger->pop_task(EXIT_FAILURE); + return; + } + ctx.logger->pop_task(EXIT_SUCCESS); +} + +void set_time(game::context& ctx, int year, int month, int day, int hour, int minute, double second) +{ + const double utc_offset = ctx.longitude / (math::two_pi / 24.0); + const double t = physics::time::gregorian::to_ut1(year, month, day, hour, minute, second, utc_offset); + set_time(ctx, t); +} + +void set_time_scale(game::context& ctx, double scale) +{ + ctx.logger->push_task("Setting time scale to " + std::to_string(scale)); + try + { + // Convert time scale from seconds to days + const double astronomical_scale = scale / physics::time::seconds_per_day; + + ctx.orbit_system->set_time_scale(astronomical_scale); + ctx.astronomy_system->set_time_scale(astronomical_scale); + } + catch (const std::exception&) + { + ctx.logger->pop_task(EXIT_FAILURE); + return; + } + ctx.logger->pop_task(EXIT_SUCCESS); +} + void load_ephemeris(game::context& ctx) { - // Load ephemeris - ctx.orbit_system->set_ephemeris(ctx.resource_manager->load>("de421.eph")); + ctx.logger->push_task("Loading ephemeris"); + + try + { + std::string ephemeris_filename; + if (ctx.config->contains("ephemeris")) + { + ephemeris_filename = (*ctx.config)["ephemeris"].get(); + } + else + { + ctx.logger->warning("No ephemeris set in config"); + ctx.logger->pop_task(EXIT_FAILURE); + return; + } + + ctx.orbit_system->set_ephemeris(ctx.resource_manager->load>(ephemeris_filename)); + } + catch (const std::exception&) + { + ctx.logger->pop_task(EXIT_FAILURE); + return; + } + + ctx.logger->pop_task(EXIT_SUCCESS); } void create_stars(game::context& ctx) { + ctx.logger->push_task("Generating fixed stars"); + // Load star catalog - string_table* star_catalog = ctx.resource_manager->load("stars.csv"); + string_table* star_catalog = nullptr; + try + { + std::string star_catalog_filename; + if (ctx.config->contains("star_catalog")) + { + star_catalog_filename = (*ctx.config)["star_catalog"].get(); + } + else + { + ctx.logger->warning("No star catalog set in config"); + ctx.logger->pop_task(EXIT_FAILURE); + return; + } + + star_catalog = ctx.resource_manager->load(star_catalog_filename); + } + catch (const std::exception&) + { + ctx.logger->pop_task(EXIT_FAILURE); + return; + } // Allocate star catalog vertex data std::size_t star_count = 0; @@ -83,22 +214,21 @@ void create_stars(game::context& ctx) { const string_table_row& catalog_row = (*star_catalog)[i]; + // Parse star catalog item double ra = 0.0; double dec = 0.0; double vmag = 0.0; - double bv_color = 0.0; - - // Parse star catalog entry + double bv = 0.0; try { ra = std::stod(catalog_row[1]); dec = std::stod(catalog_row[2]); vmag = std::stod(catalog_row[3]); - bv_color = std::stod(catalog_row[4]); + bv = std::stod(catalog_row[4]); } catch (const std::exception&) { - continue; + ctx.logger->warning("Invalid star catalog item on row " + std::to_string(i)); } // Convert right ascension and declination from degrees to radians @@ -112,7 +242,7 @@ void create_stars(game::context& ctx) double brightness = physics::light::vmag::to_brightness(vmag); // Convert color index to color temperature - double cct = color::index::bv_to_cct(bv_color); + double cct = color::index::bv_to_cct(bv); // Calculate XYZ color from color temperature double3 color_xyz = color::cct::to_xyz(cct); @@ -191,106 +321,147 @@ void create_stars(game::context& ctx) // Pass starlight illuminance to astronomy system ctx.astronomy_system->set_starlight_illuminance(starlight_illuminance); + + ctx.logger->pop_task(EXIT_SUCCESS); } void create_sun(game::context& ctx) { - // Create sun entity - entity::archetype* sun_archetype = ctx.resource_manager->load("sun.ent"); - entity::id sun_eid = sun_archetype->create(*ctx.entity_registry); - ctx.entities["sun"] = sun_eid; - - // Create sun directional light scene object - scene::directional_light* sun_light = new scene::directional_light(); - sun_light->set_color({0, 0, 0}); - sun_light->update_tweens(); - - // Create sky ambient light scene object - scene::ambient_light* sky_light = new scene::ambient_light(); - sky_light->set_color({0, 0, 0}); - sky_light->update_tweens(); + ctx.logger->push_task("Generating Sun"); - // Add sun light scene objects to surface scene - ctx.surface_scene->add_object(sun_light); - ctx.surface_scene->add_object(sky_light); + try + { + // Create sun entity + entity::archetype* sun_archetype = ctx.resource_manager->load("sun.ent"); + entity::id sun_eid = sun_archetype->create(*ctx.entity_registry); + ctx.entities["sun"] = sun_eid; + + // Create sun directional light scene object + scene::directional_light* sun_light = new scene::directional_light(); + sun_light->set_color({0, 0, 0}); + sun_light->update_tweens(); + + // Create sky ambient light scene object + scene::ambient_light* sky_light = new scene::ambient_light(); + sky_light->set_color({0, 0, 0}); + sky_light->update_tweens(); + + // Add sun light scene objects to surface scene + ctx.surface_scene->add_object(sun_light); + ctx.surface_scene->add_object(sky_light); + + // Pass direct sun light scene object to shadow map pass and astronomy system + ctx.surface_shadow_map_pass->set_light(sun_light); + ctx.astronomy_system->set_sun_light(sun_light); + ctx.astronomy_system->set_sky_light(sky_light); + } + catch (const std::exception&) + { + ctx.logger->pop_task(EXIT_FAILURE); + return; + } - // Pass direct sun light scene object to shadow map pass and astronomy system - ctx.surface_shadow_map_pass->set_light(sun_light); - ctx.astronomy_system->set_sun_light(sun_light); - ctx.astronomy_system->set_sky_light(sky_light); + ctx.logger->pop_task(EXIT_SUCCESS); } -void create_em_bary(game::context& ctx) +void create_earth_moon_system(game::context& ctx) { - // 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; + ctx.logger->push_task("Generating Earth-Moon system"); + + try + { + // 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; + + // Create Earth + create_earth(ctx); + + // Create Moon + create_moon(ctx); + } + catch (const std::exception&) + { + ctx.logger->pop_task(EXIT_FAILURE); + return; + } + + ctx.logger->pop_task(EXIT_SUCCESS); } 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; + ctx.logger->push_task("Generating Earth"); - // 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 + try + { + // 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 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 + { + //return math::random(0.0, 1.0); + return 0.0; + }; + terrain.max_lod = 0; + terrain.patch_material = nullptr; + //ctx.entity_registry->assign(earth_eid, terrain); + + // Pass earth to astronomy system as reference body + ctx.astronomy_system->set_reference_body(earth_eid); + } + catch (const std::exception&) { - //return math::random(0.0, 1.0); - return 0.0; - }; - terrain.max_lod = 0; - terrain.patch_material = nullptr; - //ctx.entity_registry->assign(earth_eid, terrain); + ctx.logger->pop_task(EXIT_FAILURE); + return; + } - // Pass earth to astronomy system as reference body - ctx.astronomy_system->set_reference_body(earth_eid); + ctx.logger->pop_task(EXIT_SUCCESS); } void create_moon(game::context& ctx) { - // Create lunar entity - entity::archetype* moon_archetype = ctx.resource_manager->load("moon.ent"); - entity::id moon_eid = moon_archetype->create(*ctx.entity_registry); - ctx.entities["moon"] = moon_eid; - - // Assign orbital parent - 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")); + ctx.logger->push_task("Generating Moon"); - // Create moon directional light scene object - scene::directional_light* moon_light = new scene::directional_light(); - moon_light->set_color({0, 0, 0}); - 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) -{ - ctx.astronomy_system->set_time(t); - ctx.orbit_system->set_time(t); -} - -void set_time_scale(game::context& ctx, double scale) -{ - static constexpr double seconds_per_day = 24.0 * 60.0 * 60.0; - scale /= seconds_per_day; + try + { + // Create lunar entity + entity::archetype* moon_archetype = ctx.resource_manager->load("moon.ent"); + entity::id moon_eid = moon_archetype->create(*ctx.entity_registry); + ctx.entities["moon"] = moon_eid; + + // Assign orbital parent + 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({0, 0, 0}); + 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); + } + catch (const std::exception&) + { + ctx.logger->pop_task(EXIT_FAILURE); + return; + } - ctx.orbit_system->set_time_scale(scale); - ctx.astronomy_system->set_time_scale(scale); + ctx.logger->pop_task(EXIT_SUCCESS); } } // namespace world diff --git a/src/game/world.hpp b/src/game/world.hpp index 8e3ccff..5cb705e 100644 --- a/src/game/world.hpp +++ b/src/game/world.hpp @@ -27,28 +27,46 @@ namespace game { /// World creation and manipulation functions. namespace world { -/// Creates an ephemeris. -void load_ephemeris(game::context& ctx); +/// Creates the cosmos. +void cosmogenesis(game::context& ctx); -/// Creates the fixed stars. -void create_stars(game::context& ctx); - -/// Creates the sun. -void create_sun(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); +/** + * Sets the location of the observer. + * + * @param ctx Game context. + * @param elevation Elevation, in meters. + * @param latitude Latitude, in radians. + * @param longitude Longitude, in radians. + */ +void set_location(game::context& ctx, double elevation, double latitude, double longitude); -/// Sets the current time. +/** + * Sets the current time. + * + * @param ctx Game context. + * @param t UT1 time, in days. + */ void set_time(game::context& ctx, double t); -/// Sets rate at which time passes. +/** + * Sets the current time. + * + * @param ctx Game context. + * @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)`. + */ +void set_time(game::context& ctx, int year, int month, int day, int hour, int minute, double second); + +/** + * Sets rate at which time passes. + * + * @param ctx Game context. + * @param scale Time scale. + */ void set_time_scale(game::context& ctx, double scale); } // namespace menu diff --git a/src/physics/time/constants.hpp b/src/physics/time/constants.hpp new file mode 100644 index 0000000..bf6238b --- /dev/null +++ b/src/physics/time/constants.hpp @@ -0,0 +1,33 @@ +/* + * 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_CONSTANTS_HPP +#define ANTKEEPER_PHYSICS_TIME_CONSTANTS_HPP + +namespace physics { +namespace time { + +/// Number of seconds per day, in seconds. +template +constexpr T seconds_per_day = T(86400); + +} // namespace time +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_TIME_CONSTANTS_HPP diff --git a/src/physics/time/gregorian.hpp b/src/physics/time/gregorian.hpp index cd23c14..f4ff2a7 100644 --- a/src/physics/time/gregorian.hpp +++ b/src/physics/time/gregorian.hpp @@ -47,7 +47,7 @@ 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); + return jdn + static_cast(hour - 12) / T(24) + static_cast(minute) / T(1440) + static_cast(second) / T(86400) - utc / T(24); } /** diff --git a/src/physics/time/time.hpp b/src/physics/time/time.hpp index 2b60160..cea682c 100644 --- a/src/physics/time/time.hpp +++ b/src/physics/time/time.hpp @@ -27,6 +27,7 @@ namespace time {} } // namespace physics +#include "constants.hpp" #include "gregorian.hpp" #include "jd.hpp" #include "ut1.hpp"