From c0c7203024c60f4ebb70958ee8727271ab4136fb Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 18 Sep 2024 02:02:47 +0100 Subject: Initial commit of code for calculating sun position Given the longitude and latitude, and a time into 2024, calculate the relative sun position suitable for providing lighting angles. Based on code from https://www.psa.es/ Loosely checked against https://www.pveducation.org/ and its close, working on faith really, but the numbers look plausible. Could do with a tidy up! --- test/Jamfile.jam | 1 + test/test-environment.cpp | 105 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 test/test-environment.cpp diff --git a/test/Jamfile.jam b/test/Jamfile.jam index 0b830a8..3ab4c4c 100644 --- a/test/Jamfile.jam +++ b/test/Jamfile.jam @@ -63,6 +63,7 @@ run test-instancing.cpp : -- : test-glContainer : test ; run perf-instancing.cpp : \< : test-instancing : benchmark test ; run test-glContainer.cpp : : : test ; run test-pack.cpp : : : test ; +run test-environment.cpp : : : test ; compile test-static-enumDetails.cpp ; compile test-static-stream_support.cpp ; explicit perf-assetFactory ; diff --git a/test/test-environment.cpp b/test/test-environment.cpp new file mode 100644 index 0000000..7b758d1 --- /dev/null +++ b/test/test-environment.cpp @@ -0,0 +1,105 @@ +#define BOOST_TEST_MODULE environment +#include +#include +#include +#include + +#include +#include + +constexpr auto degreesToRads = pi / 180.F; +constexpr auto dEarthMeanRadius = 6371.01F; // In km +constexpr auto dAstronomicalUnit = 149597890.F; // In km + +// Based on the C++ code published at https://www.psa.es/sdg/sunpos.htm +// Linked from https://www.pveducation.org/pvcdrom/properties-of-sunlight/suns-position-to-high-accuracy +Direction2D +getSunPos(const Direction2D position, const float timeOfYear2024) +{ + auto & longitude = position.x; + auto & latitude = position.y; + using std::acos; + using std::asin; + using std::atan2; + using std::cos; + using std::floor; + using std::sin; + using std::tan; + constexpr auto JD2451545 {946728000}; // which is noon 1 January 2000 Universal Time + constexpr auto J11 {1704067200}; // 31st Dec 2023, so timeOfYear2024 1 is 1st Jan etc + constexpr auto JDiff = static_cast(J11 - JD2451545); + + // Calculate difference in days between the current Julian Day + // and JD 2451545.0, which is noon 1 January 2000 Universal Time + // Calculate time of the day in UT decimal hours + const auto dDecimalHours = 24.F * (timeOfYear2024 - floor(timeOfYear2024)); + const auto dElapsedJulianDays = (JDiff + timeOfYear2024 * 86400.F) / 86400.F; + + // Calculate ecliptic coordinates (ecliptic longitude and obliquity of the + // ecliptic in radians but without limiting the angle to be less than 2*Pi + // (i.e., the result may be greater than 2*Pi) + const auto dOmega = 2.1429F - 0.0010394594F * dElapsedJulianDays; + const auto dMeanLongitude = 4.8950630F + 0.017202791698F * dElapsedJulianDays; // Radians + const auto dMeanAnomaly = 6.2400600F + 0.0172019699F * dElapsedJulianDays; + const auto dEclipticLongitude = dMeanLongitude + 0.03341607F * sin(dMeanAnomaly) + + 0.00034894F * sin(2 * dMeanAnomaly) - 0.0001134F - 0.0000203F * sin(dOmega); + const auto dEclipticObliquity = 0.4090928F - 6.2140e-9F * dElapsedJulianDays + 0.0000396F * cos(dOmega); + + // Calculate celestial coordinates ( right ascension and declination ) in radians + // but without limiting the angle to be less than 2*Pi (i.e., the result may be + // greater than 2*Pi) + const auto dSin_EclipticLongitude = sin(dEclipticLongitude); + const auto dY = cos(dEclipticObliquity) * dSin_EclipticLongitude; + const auto dX = cos(dEclipticLongitude); + auto dRightAscension = atan2(dY, dX); + if (dRightAscension < 0) { + dRightAscension = dRightAscension + two_pi; + } + const auto dDeclination = asin(sin(dEclipticObliquity) * dSin_EclipticLongitude); + + // Calculate local coordinates ( azimuth and zenith angle ) in degrees + const auto dGreenwichMeanSiderealTime = 6.6974243242F + 0.0657098283F * dElapsedJulianDays + dDecimalHours; + const auto dLocalMeanSiderealTime + = (dGreenwichMeanSiderealTime * 15.0F + (longitude / degreesToRads)) * degreesToRads; + const auto dHourAngle = dLocalMeanSiderealTime - dRightAscension; + const auto dLatitudeInRadians = latitude; + const auto dCos_Latitude = cos(dLatitudeInRadians); + const auto dSin_Latitude = sin(dLatitudeInRadians); + const auto dCos_HourAngle = cos(dHourAngle); + Direction2D udtSunCoordinates; + udtSunCoordinates.y + = (acos(dCos_Latitude * dCos_HourAngle * cos(dDeclination) + sin(dDeclination) * dSin_Latitude)); + udtSunCoordinates.x = atan2(-sin(dHourAngle), tan(dDeclination) * dCos_Latitude - dSin_Latitude * dCos_HourAngle); + if (udtSunCoordinates.x < 0) { + udtSunCoordinates.x = udtSunCoordinates.x + two_pi; + } + // Parallax Correction + const auto dParallax = (dEarthMeanRadius / dAstronomicalUnit) * sin(udtSunCoordinates.y); + udtSunCoordinates.y = half_pi - (udtSunCoordinates.y + dParallax); + + return udtSunCoordinates; +} + +using sunPosTestData = std::tuple; +constexpr Direction2D Doncaster = {-1.1, 53.5}; +constexpr Direction2D NewYork = {74.0, 40.7}; +constexpr Direction2D Syndey = {-151.2, -33.9}; + +BOOST_DATA_TEST_CASE(sun_position, + boost::unit_test::data::make({ + {{0.F, 0.F}, 1.00F, {181.52F, -66.86F}}, + {{0.F, 0.F}, 1.25F, {113.12F, -0.85F}}, + {{0.F, 0.F}, 1.50F, {177.82F, 66.97F}}, + {{0.F, 0.F}, 1.75F, {246.99F, 0.90F}}, + {{0.F, 0.F}, 2.00F, {181.52F, -67.04F}}, + {{0.F, 0.F}, 180.50F, {2.1F, 66.80F}}, + {Doncaster, 180.50F, {176.34F, 59.64F}}, + {NewYork, 180.50F, {278.04F, 27.34F}}, + {Syndey, 180.50F, {106.13F, -63.29F}}, + }), + position, timeOfYear, expSunPos) +{ + const auto sunPos = getSunPos(position * degreesToRads, timeOfYear) / degreesToRads; + BOOST_CHECK_CLOSE(sunPos.x, expSunPos.x, 1.F); + BOOST_CHECK_CLOSE(sunPos.y, expSunPos.y, 1.F); +} -- cgit v1.2.3 From e9c9d5a883e819e7799e630a883917ce4c69b41a Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 19 Sep 2024 19:53:08 +0100 Subject: Add new mathematical constants to lib --- lib/maths.h | 4 ++++ test/test-environment.cpp | 6 +----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/maths.h b/lib/maths.h index 018ef0e..3127d3c 100644 --- a/lib/maths.h +++ b/lib/maths.h @@ -42,6 +42,10 @@ constexpr auto half_pi {glm::half_pi()}; constexpr auto quarter_pi {half_pi / 2}; constexpr auto pi {glm::pi()}; constexpr auto two_pi {glm::two_pi()}; +constexpr auto degreesToRads = pi / 180.F; + +constexpr auto earthMeanRadius = 6371.01F; // In km +constexpr auto astronomicalUnit = 149597890.F; // In km template constexpr inline GlobalPosition diff --git a/test/test-environment.cpp b/test/test-environment.cpp index 7b758d1..0d02aa1 100644 --- a/test/test-environment.cpp +++ b/test/test-environment.cpp @@ -7,10 +7,6 @@ #include #include -constexpr auto degreesToRads = pi / 180.F; -constexpr auto dEarthMeanRadius = 6371.01F; // In km -constexpr auto dAstronomicalUnit = 149597890.F; // In km - // Based on the C++ code published at https://www.psa.es/sdg/sunpos.htm // Linked from https://www.pveducation.org/pvcdrom/properties-of-sunlight/suns-position-to-high-accuracy Direction2D @@ -74,7 +70,7 @@ getSunPos(const Direction2D position, const float timeOfYear2024) udtSunCoordinates.x = udtSunCoordinates.x + two_pi; } // Parallax Correction - const auto dParallax = (dEarthMeanRadius / dAstronomicalUnit) * sin(udtSunCoordinates.y); + const auto dParallax = (earthMeanRadius / astronomicalUnit) * sin(udtSunCoordinates.y); udtSunCoordinates.y = half_pi - (udtSunCoordinates.y + dParallax); return udtSunCoordinates; -- cgit v1.2.3 From b16d51e7d108c50e960d4598cb7cd47854c76f3e Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 19 Sep 2024 19:55:14 +0100 Subject: Add helper to quickly parse an ISO date/time --- lib/chronology.cpp | 12 ++++++++++++ lib/chronology.h | 2 ++ 2 files changed, 14 insertions(+) create mode 100644 lib/chronology.cpp diff --git a/lib/chronology.cpp b/lib/chronology.cpp new file mode 100644 index 0000000..8707bba --- /dev/null +++ b/lib/chronology.cpp @@ -0,0 +1,12 @@ +#include "chronology.h" + +time_t +operator""_time_t(const char * iso, size_t) +{ + struct tm tm {}; + + if (const auto end = strptime(iso, "%FT%T", &tm); !end || *end) { + throw std::invalid_argument("Invalid date"); + } + return mktime(&tm); +} diff --git a/lib/chronology.h b/lib/chronology.h index 1980116..688a1f7 100644 --- a/lib/chronology.h +++ b/lib/chronology.h @@ -1,5 +1,7 @@ #pragma once #include +#include using TickDuration = std::chrono::duration; +time_t operator""_time_t(const char * iso, size_t); -- cgit v1.2.3 From e1eae158efa45fa03d530846c16e266452f3c296 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 19 Sep 2024 20:03:09 +0100 Subject: Update getSunPos to use a standard time_t --- test/test-environment.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/test-environment.cpp b/test/test-environment.cpp index 0d02aa1..0e9e37a 100644 --- a/test/test-environment.cpp +++ b/test/test-environment.cpp @@ -4,13 +4,14 @@ #include #include +#include #include #include // Based on the C++ code published at https://www.psa.es/sdg/sunpos.htm // Linked from https://www.pveducation.org/pvcdrom/properties-of-sunlight/suns-position-to-high-accuracy Direction2D -getSunPos(const Direction2D position, const float timeOfYear2024) +getSunPos(const Direction2D position, const time_t time) { auto & longitude = position.x; auto & latitude = position.y; @@ -21,15 +22,13 @@ getSunPos(const Direction2D position, const float timeOfYear2024) using std::floor; using std::sin; using std::tan; - constexpr auto JD2451545 {946728000}; // which is noon 1 January 2000 Universal Time - constexpr auto J11 {1704067200}; // 31st Dec 2023, so timeOfYear2024 1 is 1st Jan etc - constexpr auto JDiff = static_cast(J11 - JD2451545); + static const auto JD2451545 = "2000-01-01T12:00:00"_time_t; // Calculate difference in days between the current Julian Day // and JD 2451545.0, which is noon 1 January 2000 Universal Time // Calculate time of the day in UT decimal hours - const auto dDecimalHours = 24.F * (timeOfYear2024 - floor(timeOfYear2024)); - const auto dElapsedJulianDays = (JDiff + timeOfYear2024 * 86400.F) / 86400.F; + const auto dDecimalHours = static_cast(time % 86400) / 3600.F; + const auto dElapsedJulianDays = static_cast(time - JD2451545) / 86400.F; // Calculate ecliptic coordinates (ecliptic longitude and obliquity of the // ecliptic in radians but without limiting the angle to be less than 2*Pi @@ -76,22 +75,23 @@ getSunPos(const Direction2D position, const float timeOfYear2024) return udtSunCoordinates; } -using sunPosTestData = std::tuple; +using sunPosTestData = std::tuple; constexpr Direction2D Doncaster = {-1.1, 53.5}; constexpr Direction2D NewYork = {74.0, 40.7}; constexpr Direction2D Syndey = {-151.2, -33.9}; +constexpr Direction2D EqGM = {}; BOOST_DATA_TEST_CASE(sun_position, boost::unit_test::data::make({ - {{0.F, 0.F}, 1.00F, {181.52F, -66.86F}}, - {{0.F, 0.F}, 1.25F, {113.12F, -0.85F}}, - {{0.F, 0.F}, 1.50F, {177.82F, 66.97F}}, - {{0.F, 0.F}, 1.75F, {246.99F, 0.90F}}, - {{0.F, 0.F}, 2.00F, {181.52F, -67.04F}}, - {{0.F, 0.F}, 180.50F, {2.1F, 66.80F}}, - {Doncaster, 180.50F, {176.34F, 59.64F}}, - {NewYork, 180.50F, {278.04F, 27.34F}}, - {Syndey, 180.50F, {106.13F, -63.29F}}, + {EqGM, "2024-01-02T00:00:00"_time_t, {181.52F, -66.86F}}, + {EqGM, "2024-01-02T06:00:00"_time_t, {113.12F, -0.85F}}, + {EqGM, "2024-01-02T12:00:00"_time_t, {177.82F, 66.97F}}, + {EqGM, "2024-01-02T18:00:00"_time_t, {246.99F, 0.90F}}, + {EqGM, "2024-01-03T00:00:00"_time_t, {181.52F, -67.04F}}, + {EqGM, "2024-06-29T12:00:00"_time_t, {2.1F, 66.80F}}, + {Doncaster, "2024-06-29T12:00:00"_time_t, {176.34F, 59.64F}}, + {NewYork, "2024-06-29T12:00:00"_time_t, {278.04F, 27.34F}}, + {Syndey, "2024-06-29T12:00:00"_time_t, {106.13F, -63.29F}}, }), position, timeOfYear, expSunPos) { -- cgit v1.2.3 From 489fa7f930689dc9ff271138e613a8d68d88ee45 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 20 Sep 2024 20:17:32 +0100 Subject: Add basic environment object Will hold world time/date, weather, location etc --- game/environment.cpp | 18 ++++++++++++++++++ game/environment.h | 16 ++++++++++++++++ game/gamestate.cpp | 3 +++ game/gamestate.h | 2 ++ test/test-render.cpp | 8 ++++++++ ui/gameMainWindow.cpp | 8 ++++---- 6 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 game/environment.cpp create mode 100644 game/environment.h diff --git a/game/environment.cpp b/game/environment.cpp new file mode 100644 index 0000000..fd2bfd4 --- /dev/null +++ b/game/environment.cpp @@ -0,0 +1,18 @@ +#include "environment.h" +#include +#include + +Environment::Environment() : worldTime {"2024-01-01T12:00:00"_time_t} { } + +void +Environment::tick(TickDuration) +{ + worldTime += 1; +} + +void +Environment::render(const SceneRenderer & renderer, const SceneProvider & scene) const +{ + renderer.setAmbientLight({0.5F, 0.5F, 0.5F}); + renderer.setDirectionalLight({0.6F, 0.6F, 0.6F}, {-1, 1, -1}, scene); +} diff --git a/game/environment.h b/game/environment.h new file mode 100644 index 0000000..62eedb8 --- /dev/null +++ b/game/environment.h @@ -0,0 +1,16 @@ +#pragma once + +#include "worldobject.h" + +class SceneRenderer; +class SceneProvider; + +class Environment : public WorldObject { +public: + Environment(); + void tick(TickDuration elapsed) override; + void render(const SceneRenderer &, const SceneProvider &) const; + +private: + time_t worldTime; +}; diff --git a/game/gamestate.cpp b/game/gamestate.cpp index fcd4248..910e8a7 100644 --- a/game/gamestate.cpp +++ b/game/gamestate.cpp @@ -1,4 +1,5 @@ #include "gamestate.h" +#include "environment.h" #include GameState * gameState {nullptr}; @@ -7,6 +8,8 @@ GameState::GameState() { assert(!gameState); gameState = this; + + environment = world.create(); } GameState::~GameState() diff --git a/game/gamestate.h b/game/gamestate.h index f07f844..892aa69 100644 --- a/game/gamestate.h +++ b/game/gamestate.h @@ -7,6 +7,7 @@ class WorldObject; class GeoData; +class Environment; class GameState { public: @@ -17,6 +18,7 @@ public: Collection world; std::shared_ptr geoData; + std::shared_ptr environment; AssetFactory::Assets assets; }; diff --git a/test/test-render.cpp b/test/test-render.cpp index b9a809e..ea53708 100644 --- a/test/test-render.cpp +++ b/test/test-render.cpp @@ -1,3 +1,4 @@ +#include "game/environment.h" #define BOOST_TEST_MODULE test_render #include "testHelpers.h" @@ -33,6 +34,7 @@ class TestScene : public SceneProvider { std::shared_ptr plant1; RailLinks rail; std::shared_ptr gd = std::make_shared(GeoData::createFlat({0, 0}, {1000000, 1000000}, 1)); + std::shared_ptr env = std::make_shared(); Terrain terrain {gd}; Water water {gd}; @@ -68,6 +70,12 @@ public: { } + void + environment(const SceneShader &, const SceneRenderer & r) const override + { + env->render(r, *this); + } + void shadows(const ShadowMapper & shadowMapper) const override { diff --git a/ui/gameMainWindow.cpp b/ui/gameMainWindow.cpp index 6168504..c53300b 100644 --- a/ui/gameMainWindow.cpp +++ b/ui/gameMainWindow.cpp @@ -1,16 +1,17 @@ #include "gameMainWindow.h" #include "editNetwork.h" #include "gameMainSelector.h" -#include "gfx/camera_controller.h" #include "manualCameraController.h" #include "modeHelper.h" #include "toolbar.h" #include "window.h" #include #include +#include #include #include #include // IWYU pragma: keep +#include #include #include #include @@ -65,10 +66,9 @@ GameMainWindow::content(const SceneShader & shader) const } void -GameMainWindow::environment(const SceneShader & s, const SceneRenderer & r) const +GameMainWindow::environment(const SceneShader &, const SceneRenderer & r) const { - // default for now - SceneProvider::environment(s, r); + gameState->environment->render(r, *this); } void -- cgit v1.2.3 From 9b3eff2ee85ca6627342cfbbe1ac3ba988dc377f Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 20 Sep 2024 20:27:51 +0100 Subject: Move getSunPos to Environment --- game/environment.cpp | 67 +++++++++++++++++++++++++++++++++++++++++++++ game/environment.h | 2 ++ test/test-environment.cpp | 70 ++--------------------------------------------- 3 files changed, 71 insertions(+), 68 deletions(-) diff --git a/game/environment.cpp b/game/environment.cpp index fd2bfd4..665c11b 100644 --- a/game/environment.cpp +++ b/game/environment.cpp @@ -16,3 +16,70 @@ Environment::render(const SceneRenderer & renderer, const SceneProvider & scene) renderer.setAmbientLight({0.5F, 0.5F, 0.5F}); renderer.setDirectionalLight({0.6F, 0.6F, 0.6F}, {-1, 1, -1}, scene); } + +// Based on the C++ code published at https://www.psa.es/sdg/sunpos.htm +// Linked from https://www.pveducation.org/pvcdrom/properties-of-sunlight/suns-position-to-high-accuracy +Direction2D +Environment::getSunPos(const Direction2D position, const time_t time) +{ + auto & longitude = position.x; + auto & latitude = position.y; + using std::acos; + using std::asin; + using std::atan2; + using std::cos; + using std::floor; + using std::sin; + using std::tan; + static const auto JD2451545 = "2000-01-01T12:00:00"_time_t; + + // Calculate difference in days between the current Julian Day + // and JD 2451545.0, which is noon 1 January 2000 Universal Time + // Calculate time of the day in UT decimal hours + const auto dDecimalHours = static_cast(time % 86400) / 3600.F; + const auto dElapsedJulianDays = static_cast(time - JD2451545) / 86400.F; + + // Calculate ecliptic coordinates (ecliptic longitude and obliquity of the + // ecliptic in radians but without limiting the angle to be less than 2*Pi + // (i.e., the result may be greater than 2*Pi) + const auto dOmega = 2.1429F - 0.0010394594F * dElapsedJulianDays; + const auto dMeanLongitude = 4.8950630F + 0.017202791698F * dElapsedJulianDays; // Radians + const auto dMeanAnomaly = 6.2400600F + 0.0172019699F * dElapsedJulianDays; + const auto dEclipticLongitude = dMeanLongitude + 0.03341607F * sin(dMeanAnomaly) + + 0.00034894F * sin(2 * dMeanAnomaly) - 0.0001134F - 0.0000203F * sin(dOmega); + const auto dEclipticObliquity = 0.4090928F - 6.2140e-9F * dElapsedJulianDays + 0.0000396F * cos(dOmega); + + // Calculate celestial coordinates ( right ascension and declination ) in radians + // but without limiting the angle to be less than 2*Pi (i.e., the result may be + // greater than 2*Pi) + const auto dSin_EclipticLongitude = sin(dEclipticLongitude); + const auto dY = cos(dEclipticObliquity) * dSin_EclipticLongitude; + const auto dX = cos(dEclipticLongitude); + auto dRightAscension = atan2(dY, dX); + if (dRightAscension < 0) { + dRightAscension = dRightAscension + two_pi; + } + const auto dDeclination = asin(sin(dEclipticObliquity) * dSin_EclipticLongitude); + + // Calculate local coordinates ( azimuth and zenith angle ) in degrees + const auto dGreenwichMeanSiderealTime = 6.6974243242F + 0.0657098283F * dElapsedJulianDays + dDecimalHours; + const auto dLocalMeanSiderealTime + = (dGreenwichMeanSiderealTime * 15.0F + (longitude / degreesToRads)) * degreesToRads; + const auto dHourAngle = dLocalMeanSiderealTime - dRightAscension; + const auto dLatitudeInRadians = latitude; + const auto dCos_Latitude = cos(dLatitudeInRadians); + const auto dSin_Latitude = sin(dLatitudeInRadians); + const auto dCos_HourAngle = cos(dHourAngle); + Direction2D udtSunCoordinates; + udtSunCoordinates.y + = (acos(dCos_Latitude * dCos_HourAngle * cos(dDeclination) + sin(dDeclination) * dSin_Latitude)); + udtSunCoordinates.x = atan2(-sin(dHourAngle), tan(dDeclination) * dCos_Latitude - dSin_Latitude * dCos_HourAngle); + if (udtSunCoordinates.x < 0) { + udtSunCoordinates.x = udtSunCoordinates.x + two_pi; + } + // Parallax Correction + const auto dParallax = (earthMeanRadius / astronomicalUnit) * sin(udtSunCoordinates.y); + udtSunCoordinates.y = half_pi - (udtSunCoordinates.y + dParallax); + + return udtSunCoordinates; +} diff --git a/game/environment.h b/game/environment.h index 62eedb8..a6f3036 100644 --- a/game/environment.h +++ b/game/environment.h @@ -1,5 +1,6 @@ #pragma once +#include "config/types.h" #include "worldobject.h" class SceneRenderer; @@ -10,6 +11,7 @@ public: Environment(); void tick(TickDuration elapsed) override; void render(const SceneRenderer &, const SceneProvider &) const; + static Direction2D getSunPos(const Direction2D position, const time_t time); private: time_t worldTime; diff --git a/test/test-environment.cpp b/test/test-environment.cpp index 0e9e37a..b6e0e4f 100644 --- a/test/test-environment.cpp +++ b/test/test-environment.cpp @@ -6,75 +6,9 @@ #include #include +#include #include -// Based on the C++ code published at https://www.psa.es/sdg/sunpos.htm -// Linked from https://www.pveducation.org/pvcdrom/properties-of-sunlight/suns-position-to-high-accuracy -Direction2D -getSunPos(const Direction2D position, const time_t time) -{ - auto & longitude = position.x; - auto & latitude = position.y; - using std::acos; - using std::asin; - using std::atan2; - using std::cos; - using std::floor; - using std::sin; - using std::tan; - static const auto JD2451545 = "2000-01-01T12:00:00"_time_t; - - // Calculate difference in days between the current Julian Day - // and JD 2451545.0, which is noon 1 January 2000 Universal Time - // Calculate time of the day in UT decimal hours - const auto dDecimalHours = static_cast(time % 86400) / 3600.F; - const auto dElapsedJulianDays = static_cast(time - JD2451545) / 86400.F; - - // Calculate ecliptic coordinates (ecliptic longitude and obliquity of the - // ecliptic in radians but without limiting the angle to be less than 2*Pi - // (i.e., the result may be greater than 2*Pi) - const auto dOmega = 2.1429F - 0.0010394594F * dElapsedJulianDays; - const auto dMeanLongitude = 4.8950630F + 0.017202791698F * dElapsedJulianDays; // Radians - const auto dMeanAnomaly = 6.2400600F + 0.0172019699F * dElapsedJulianDays; - const auto dEclipticLongitude = dMeanLongitude + 0.03341607F * sin(dMeanAnomaly) - + 0.00034894F * sin(2 * dMeanAnomaly) - 0.0001134F - 0.0000203F * sin(dOmega); - const auto dEclipticObliquity = 0.4090928F - 6.2140e-9F * dElapsedJulianDays + 0.0000396F * cos(dOmega); - - // Calculate celestial coordinates ( right ascension and declination ) in radians - // but without limiting the angle to be less than 2*Pi (i.e., the result may be - // greater than 2*Pi) - const auto dSin_EclipticLongitude = sin(dEclipticLongitude); - const auto dY = cos(dEclipticObliquity) * dSin_EclipticLongitude; - const auto dX = cos(dEclipticLongitude); - auto dRightAscension = atan2(dY, dX); - if (dRightAscension < 0) { - dRightAscension = dRightAscension + two_pi; - } - const auto dDeclination = asin(sin(dEclipticObliquity) * dSin_EclipticLongitude); - - // Calculate local coordinates ( azimuth and zenith angle ) in degrees - const auto dGreenwichMeanSiderealTime = 6.6974243242F + 0.0657098283F * dElapsedJulianDays + dDecimalHours; - const auto dLocalMeanSiderealTime - = (dGreenwichMeanSiderealTime * 15.0F + (longitude / degreesToRads)) * degreesToRads; - const auto dHourAngle = dLocalMeanSiderealTime - dRightAscension; - const auto dLatitudeInRadians = latitude; - const auto dCos_Latitude = cos(dLatitudeInRadians); - const auto dSin_Latitude = sin(dLatitudeInRadians); - const auto dCos_HourAngle = cos(dHourAngle); - Direction2D udtSunCoordinates; - udtSunCoordinates.y - = (acos(dCos_Latitude * dCos_HourAngle * cos(dDeclination) + sin(dDeclination) * dSin_Latitude)); - udtSunCoordinates.x = atan2(-sin(dHourAngle), tan(dDeclination) * dCos_Latitude - dSin_Latitude * dCos_HourAngle); - if (udtSunCoordinates.x < 0) { - udtSunCoordinates.x = udtSunCoordinates.x + two_pi; - } - // Parallax Correction - const auto dParallax = (earthMeanRadius / astronomicalUnit) * sin(udtSunCoordinates.y); - udtSunCoordinates.y = half_pi - (udtSunCoordinates.y + dParallax); - - return udtSunCoordinates; -} - using sunPosTestData = std::tuple; constexpr Direction2D Doncaster = {-1.1, 53.5}; constexpr Direction2D NewYork = {74.0, 40.7}; @@ -95,7 +29,7 @@ BOOST_DATA_TEST_CASE(sun_position, }), position, timeOfYear, expSunPos) { - const auto sunPos = getSunPos(position * degreesToRads, timeOfYear) / degreesToRads; + const auto sunPos = Environment::getSunPos(position * degreesToRads, timeOfYear) / degreesToRads; BOOST_CHECK_CLOSE(sunPos.x, expSunPos.x, 1.F); BOOST_CHECK_CLOSE(sunPos.y, expSunPos.y, 1.F); } -- cgit v1.2.3 From 3573ecff39a617cc4084660aba2baf6f4bebfaaf Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 22 Sep 2024 12:54:48 +0100 Subject: Calculate sunlight direction from worldTime --- game/environment.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/game/environment.cpp b/game/environment.cpp index 665c11b..1ef9bcb 100644 --- a/game/environment.cpp +++ b/game/environment.cpp @@ -7,14 +7,16 @@ Environment::Environment() : worldTime {"2024-01-01T12:00:00"_time_t} { } void Environment::tick(TickDuration) { - worldTime += 1; + worldTime += 50; } void Environment::render(const SceneRenderer & renderer, const SceneProvider & scene) const { + const auto sunPos = getSunPos({}, worldTime); + const auto sunDir = (glm::mat3 {rotate_yp({sunPos.y + pi, sunPos.x})} * north); renderer.setAmbientLight({0.5F, 0.5F, 0.5F}); - renderer.setDirectionalLight({0.6F, 0.6F, 0.6F}, {-1, 1, -1}, scene); + renderer.setDirectionalLight({0.6F, 0.6F, 0.6F}, sunDir, scene); } // Based on the C++ code published at https://www.psa.es/sdg/sunpos.htm -- cgit v1.2.3 From c5cf04a2c6c2e1c1315f1c6125c06ab1bbbdb045 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 22 Sep 2024 15:13:13 +0100 Subject: Adjust light colour as sun rises/sets This is a bit made-up-maths/numbers, but it looks reasonable. --- game/environment.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/game/environment.cpp b/game/environment.cpp index 1ef9bcb..19aad84 100644 --- a/game/environment.cpp +++ b/game/environment.cpp @@ -13,10 +13,17 @@ Environment::tick(TickDuration) void Environment::render(const SceneRenderer & renderer, const SceneProvider & scene) const { + constexpr RGB baseAmbient {0.1F}, baseDirectional {0.0F}; + constexpr RGB relativeAmbient {0.3F, 0.3F, 0.4F}, relativeDirectional {0.6F, 0.6F, 0.5F}; + const auto sunPos = getSunPos({}, worldTime); const auto sunDir = (glm::mat3 {rotate_yp({sunPos.y + pi, sunPos.x})} * north); - renderer.setAmbientLight({0.5F, 0.5F, 0.5F}); - renderer.setDirectionalLight({0.6F, 0.6F, 0.6F}, sunDir, scene); + const auto vertical = -std::min(0.F, sunDir.z - 0.1F); + const auto ambient = baseAmbient + relativeAmbient * vertical; + const auto directional = baseDirectional + relativeDirectional * vertical; + + renderer.setAmbientLight(ambient); + renderer.setDirectionalLight(directional, sunDir, scene); } // Based on the C++ code published at https://www.psa.es/sdg/sunpos.htm -- cgit v1.2.3