diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 4f6f52a81a..2ee77458d4 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -352,6 +352,30 @@ namespace MWWorld mWeather = 0; } + MoonModel::MoonModel(float fadeInStart, float fadeInFinish, float fadeOutStart, float fadeOutFinish, + float axisOffset, float speed, float dailyIncrement, float fadeStartAngle, float fadeEndAngle, + float moonShadowEarlyFadeAngle) + { + mFadeInStart = fadeInStart; + mFadeInFinish = fadeInFinish; + mFadeOutStart = fadeOutStart; + mFadeOutFinish = fadeOutFinish; + mAxisOffset = axisOffset; + mDailyIncrement = dailyIncrement; + mFadeStartAngle = fadeStartAngle; + mFadeEndAngle = fadeEndAngle; + mMoonShadowEarlyFadeAngle = moonShadowEarlyFadeAngle; + + // Morrowind appears to have a minimum speed to avoid situations where the moon can't + // complete a full rotation in a single 24-hour period. The reverse-engineered formula is + // 180 degrees (full hemisphere) / 23 hours / 15 degrees (1 hour travel at speed 1.0). + mSpeed = std::max(speed, (180.0f / 23.0f / 15.0f)); + + // Morrowind appears to reduce mDailyIncrement with modulo 24.0f to avoid situations where + // the moon would increment more than an entire rotation in a single day. + mDailyIncrement = std::fmod(mDailyIncrement, 24.0f); + } + MoonModel::MoonModel(const std::string& name) : mFadeInStart(Fallback::Map::getFloat("Moons_" + name + "_Fade_In_Start")) , mFadeInFinish(Fallback::Map::getFloat("Moons_" + name + "_Fade_In_Finish")) @@ -364,14 +388,19 @@ namespace MWWorld , mFadeEndAngle(Fallback::Map::getFloat("Moons_" + name + "_Fade_End_Angle")) , mMoonShadowEarlyFadeAngle(Fallback::Map::getFloat("Moons_" + name + "_Moon_Shadow_Early_Fade_Angle")) { - // Morrowind appears to have a minimum speed in order to avoid situations where the moon couldn't conceivably - // complete a rotation in a single 24 hour period. The value of 180/23 was deduced from reverse engineering. - mSpeed = std::min(mSpeed, 180.0f / 23.0f); + // Morrowind appears to have a minimum speed to avoid situations where the moon can't + // complete a full rotation in a single 24-hour period. The reverse-engineered formula is + // 180 degrees (full hemisphere) / 23 hours / 15 degrees (1 hour travel at speed 1.0). + mSpeed = std::max(mSpeed, (180.0f / 23.0f / 15.0f)); + + // Morrowind appears to reduce mDailyIncrement with modulo 24.0f to avoid situations where + // the moon would increment more than an entire rotation in a single day. + mDailyIncrement = std::fmod(mDailyIncrement, 24.0f); } MWRender::MoonState MoonModel::calculateState(const TimeStamp& gameTime) const { - float rotationFromHorizon = angle(gameTime); + float rotationFromHorizon = angle(gameTime.getDay(), gameTime.getHour()); MWRender::MoonState state = { rotationFromHorizon, mAxisOffset, // Reverse engineered from Morrowind's scene graph rotation matrices. phase(gameTime), shadowBlend(rotationFromHorizon), @@ -380,7 +409,7 @@ namespace MWWorld return state; } - inline float MoonModel::angle(const TimeStamp& gameTime) const + inline float MoonModel::angle(int gameDay, float gameHour) const { // Morrowind's moons start travel on one side of the horizon (let's call it H-rise) and travel 180 degrees to // the opposite horizon (let's call it H-set). Upon reaching H-set, they reset to H-rise until the next moon @@ -390,49 +419,83 @@ namespace MWWorld // 1. Moon rises and then sets in one day. // 2. Moon sets and doesn't rise in one day (occurs when the moon rise hour is >= 24). // 3. Moon sets and then rises in one day. - float moonRiseHourToday = moonRiseHour(gameTime.getDay()); - float moonRiseAngleToday = 0; + float moonRiseHourToday = moonRiseHour(gameDay); + float moonRiseAngleToday = 0.0f; - if (gameTime.getHour() < moonRiseHourToday) + if (gameHour < moonRiseHourToday) { - float moonRiseHourYesterday = moonRiseHour(gameTime.getDay() - 1); - if (moonRiseHourYesterday < 24) + // Rise hour increases by mDailyIncrement each day, so yesterday's is easy to calculate + float moonRiseHourYesterday = moonRiseHourToday - mDailyIncrement; + if (moonRiseHourYesterday < 24.0f) { - float moonRiseAngleYesterday = rotation(24 - moonRiseHourYesterday); - if (moonRiseAngleYesterday < 180) + // Morrowind offsets the increment by -1 when the previous day's visible point crosses into the next + // day. The offset lasts from this point until the next 24-day loop starts. To find this point we add + // mDailyIncrement to the previous visible point and check the result. + float moonShadowEarlyFadeAngle1 = mFadeEndAngle - mMoonShadowEarlyFadeAngle; + float timeToVisible = moonShadowEarlyFadeAngle1 / rotation(1.0f); + float cycleOffset = moonRiseHourYesterday + timeToVisible > 24.0f ? mDailyIncrement : 0.0f; + + float moonRiseAngleYesterday = rotation(24.0f - (moonRiseHourYesterday + cycleOffset)); + if (moonRiseAngleYesterday < 180.0f) { // The moon rose but did not set yesterday, so accumulate yesterday's angle with how much we've // travelled today. - moonRiseAngleToday = rotation(gameTime.getHour()) + moonRiseAngleYesterday; + moonRiseAngleToday = rotation(gameHour) + moonRiseAngleYesterday; } } } else { - moonRiseAngleToday = rotation(gameTime.getHour() - moonRiseHourToday); + moonRiseAngleToday = rotation(gameHour - moonRiseHourToday); } - if (moonRiseAngleToday >= 180) + if (moonRiseAngleToday >= 180.0f) { // The moon set today, reset the angle to the horizon. - moonRiseAngleToday = 0; + moonRiseAngleToday = 0.0f; } return moonRiseAngleToday; } - inline float MoonModel::moonRiseHour(unsigned int daysPassed) const + inline float MoonModel::moonPhaseHour(int gameDay) const { + // Morrowind delays moon phase changes until one of these is true: + // * The moon is invisible at midnight. + // * The moon reached moonShadowEarlyFadeAngle2 one daily increment ago (therefore invisible). + if (!isVisible(gameDay, 0.0f)) + return 0.0f; + else + { + // Calculate the angle at which the moon becomes transparent and the starting angle. + float moonShadowEarlyFadeAngle2 = (180.0f - mFadeEndAngle) + mMoonShadowEarlyFadeAngle; + float midnightAngle = angle(gameDay, 0.0f); + + // We can assume that moonShadowEarlyFadeAngle2 > midnightAngle, because the opposite + // case would make the moon invisible at midnight, which is checked above. + return ((moonShadowEarlyFadeAngle2 - midnightAngle) / rotation(1.0f)) + std::max(mDailyIncrement, 0.0f); + } + } + + inline float MoonModel::moonRiseHour(int gameDay) const + { + if (mDailyIncrement == 0.0f) + return 0.0f; + // This arises from the start date of 16 Last Seed, 427 // TODO: Find an alternate formula that doesn't rely on this day being fixed. - static const unsigned int startDay = 16; + constexpr int startDay = 16; + + // This formula finds the number of missed increments necessary to make the rise hour a 24-day loop. + // The offset increases on the first day of the loop and is multiplied by the number of completed loops. + float incrementOffset = (24.0f - std::abs(24.0f / mDailyIncrement)) * std::floor((gameDay + startDay) / 24.0f); // This odd formula arises from the fact that on 16 Last Seed, 17 increments have occurred, meaning // that upon starting a new game, it must only calculate the moon phase as far back as 1 Last Seed. // Note that we don't modulo after adding the latest daily increment because other calculations need to // know if doing so would cause the moon rise to be postponed until the next day (which happens when // the moon rise hour is >= 24 in Morrowind). - return mDailyIncrement + std::fmod((daysPassed - 1 + startDay) * mDailyIncrement, 24.0f); + return mDailyIncrement + std::fmod((gameDay - 1 + startDay - incrementOffset) * mDailyIncrement, 24.0f); } inline float MoonModel::rotation(float hours) const @@ -449,12 +512,18 @@ namespace MWWorld // phase cycle. // If the moon didn't rise yet today, use yesterday's moon phase. - if (gameTime.getHour() < moonRiseHour(gameTime.getDay())) + if (gameTime.getHour() < moonPhaseHour(gameTime.getDay())) return static_cast((gameTime.getDay() / 3) % 8); else return static_cast(((gameTime.getDay() + 1) / 3) % 8); } + inline bool MoonModel::isVisible(int gameDay, float gameHour) const + { + // Moons are "visible" when their alpha value is non-zero. + return hourlyAlpha(gameHour) > 0.f && earlyMoonShadowAlpha(angle(gameDay, gameHour)) > 0.f; + } + inline float MoonModel::shadowBlend(float angle) const { // The Fade End Angle and Fade Start Angle describe a region where the moon transitions from a solid disk @@ -480,6 +549,10 @@ namespace MWWorld inline float MoonModel::hourlyAlpha(float gameHour) const { + // Morrowind culls the moon one minute before mFadeOutFinish + constexpr float oneMinute = 0.0167f; + float adjustedFadeOutFinish = mFadeOutFinish - oneMinute; + // The Fade Out Start / Finish and Fade In Start / Finish describe the hours at which the moon // appears and disappears. // Depending on the current hour, the following values describe how transparent the moon is. @@ -487,9 +560,9 @@ namespace MWWorld // 2. From Fade Out Finish to Fade In Start: 0 (transparent) // 3. From Fade In Start to Fade In Finish: 0..1 // 4. From Fade In Finish to Fade Out Start: 1 (solid) - if ((gameHour >= mFadeOutStart) && (gameHour < mFadeOutFinish)) - return (mFadeOutFinish - gameHour) / (mFadeOutFinish - mFadeOutStart); - else if ((gameHour >= mFadeOutFinish) && (gameHour < mFadeInStart)) + if ((gameHour >= mFadeOutStart) && (gameHour < adjustedFadeOutFinish)) + return (adjustedFadeOutFinish - gameHour) / (adjustedFadeOutFinish - mFadeOutStart); + else if ((gameHour >= adjustedFadeOutFinish) && (gameHour < mFadeInStart)) return 0.0f; else if ((gameHour >= mFadeInStart) && (gameHour < mFadeInFinish)) return (gameHour - mFadeInStart) / (mFadeInFinish - mFadeInStart); diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 0643240dcd..7c27a10316 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -251,6 +251,9 @@ namespace MWWorld { public: MoonModel(const std::string& name); + MoonModel(float fadeInStart, float fadeInFinish, float fadeOutStart, float fadeOutFinish, float axisOffset, + float speed, float dailyIncrement, float fadeStartAngle, float fadeEndAngle, + float moonShadowEarlyFadeAngle); MWRender::MoonState calculateState(const TimeStamp& gameTime) const; @@ -266,10 +269,12 @@ namespace MWWorld float mFadeEndAngle; float mMoonShadowEarlyFadeAngle; - float angle(const TimeStamp& gameTime) const; - float moonRiseHour(unsigned int daysPassed) const; + float angle(int gameDay, float gameHour) const; + float moonPhaseHour(int gameDay) const; + float moonRiseHour(int gameDay) const; float rotation(float hours) const; MWRender::MoonState::Phase phase(const TimeStamp& gameTime) const; + bool isVisible(int gameDay, float gameHour) const; float shadowBlend(float angle) const; float hourlyAlpha(float gameHour) const; float earlyMoonShadowAlpha(float angle) const; diff --git a/apps/openmw_tests/CMakeLists.txt b/apps/openmw_tests/CMakeLists.txt index 9b57113110..5cfee9ce4b 100644 --- a/apps/openmw_tests/CMakeLists.txt +++ b/apps/openmw_tests/CMakeLists.txt @@ -10,6 +10,7 @@ file(GLOB UNITTEST_SRC_FILES mwworld/testduration.cpp mwworld/testtimestamp.cpp mwworld/testptr.cpp + mwworld/testweather.cpp mwdialogue/test_keywordsearch.cpp diff --git a/apps/openmw_tests/mwworld/testweather.cpp b/apps/openmw_tests/mwworld/testweather.cpp new file mode 100644 index 0000000000..4b7fd8f9e2 --- /dev/null +++ b/apps/openmw_tests/mwworld/testweather.cpp @@ -0,0 +1,732 @@ +#include + +#include + +#include "apps/openmw/mwworld/timestamp.hpp" +#include "apps/openmw/mwworld/weather.hpp" + +namespace MWWorld +{ + namespace + { + // MASSER PHASES + + TEST(MWWorldWeatherTest, masserPhasesFullToWaningGibbousAtCorrectTimes) + { + float dailyIncrement = 1.0f; + float speed = 0.5f; + float fadeEndAngle = 40.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 14.0f; + float fadeInFinish = 15.0f; + float fadeOutStart = 7.0f; + float fadeOutFinish = 10.0f; + float axisOffset = 35.0f; + + // Days 2 and 26, 11:57 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 2 + 11.0f + 56.0f / 60.0f); + timeStampAfter += (24.0f * 2 + 11.0f + 58.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 26 + 11.0f + 56.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 26 + 11.0f + 58.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_EQ(beforeState.mPhase, static_cast(0)); + EXPECT_EQ(afterState.mPhase, static_cast(1)); + EXPECT_EQ(beforeStatePostLoop.mPhase, static_cast(0)); + EXPECT_EQ(afterStatePostLoop.mPhase, static_cast(1)); + } + + TEST(MWWorldWeatherTest, masserPhasesWaningGibbousToThirdQuarterAtCorrectTimes) + { + float dailyIncrement = 1.0f; + float speed = 0.5f; + float fadeEndAngle = 40.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 14.0f; + float fadeInFinish = 15.0f; + float fadeOutStart = 7.0f; + float fadeOutFinish = 10.0f; + float axisOffset = 35.0f; + + // Days 5 and 29, 0:00 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 4 + 23.0f + 59.0f / 60.0f); + timeStampAfter += (24.0f * 5 + 0.0f + 1.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 28 + 23.0f + 59.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 29 + 0.0f + 1.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_EQ(beforeState.mPhase, static_cast(1)); + EXPECT_EQ(afterState.mPhase, static_cast(2)); + EXPECT_EQ(beforeStatePostLoop.mPhase, static_cast(1)); + EXPECT_EQ(afterStatePostLoop.mPhase, static_cast(2)); + } + + TEST(MWWorldWeatherTest, masserPhasesThirdQuarterToWaningCrescentAtCorrectTimes) + { + float dailyIncrement = 1.0f; + float speed = 0.5f; + float fadeEndAngle = 40.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 14.0f; + float fadeInFinish = 15.0f; + float fadeOutStart = 7.0f; + float fadeOutFinish = 10.0f; + float axisOffset = 35.0f; + + // Days 8 and 32, 0:00 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 7 + 23.0f + 59.0f / 60.0f); + timeStampAfter += (24.0f * 8 + 0.0f + 1.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 31 + 23.0f + 59.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 32 + 0.0f + 1.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_EQ(beforeState.mPhase, static_cast(2)); + EXPECT_EQ(afterState.mPhase, static_cast(3)); + EXPECT_EQ(beforeStatePostLoop.mPhase, static_cast(2)); + EXPECT_EQ(afterStatePostLoop.mPhase, static_cast(3)); + } + + TEST(MWWorldWeatherTest, masserPhasesWaningCrescentToNewAtCorrectTimes) + { + float dailyIncrement = 1.0f; + float speed = 0.5f; + float fadeEndAngle = 40.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 14.0f; + float fadeInFinish = 15.0f; + float fadeOutStart = 7.0f; + float fadeOutFinish = 10.0f; + float axisOffset = 35.0f; + + // Days 11 and 35, 0:00 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 10 + 23.0f + 59.0f / 60.0f); + timeStampAfter += (24.0f * 11 + 0.0f + 1.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 34 + 23.0f + 59.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 35 + 0.0f + 1.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_EQ(beforeState.mPhase, static_cast(3)); + EXPECT_EQ(afterState.mPhase, static_cast(4)); + EXPECT_EQ(beforeStatePostLoop.mPhase, static_cast(3)); + EXPECT_EQ(afterStatePostLoop.mPhase, static_cast(4)); + } + + TEST(MWWorldWeatherTest, masserPhasesNewToWaxingCrescentAtCorrectTimes) + { + float dailyIncrement = 1.0f; + float speed = 0.5f; + float fadeEndAngle = 40.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 14.0f; + float fadeInFinish = 15.0f; + float fadeOutStart = 7.0f; + float fadeOutFinish = 10.0f; + float axisOffset = 35.0f; + + // Days 14 and 38, 0:00 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 13 + 23.0f + 59.0f / 60.0f); + timeStampAfter += (24.0f * 14 + 0.0f + 1.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 37 + 23.0f + 59.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 38 + 0.0f + 1.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_EQ(beforeState.mPhase, static_cast(4)); + EXPECT_EQ(afterState.mPhase, static_cast(5)); + EXPECT_EQ(beforeStatePostLoop.mPhase, static_cast(4)); + EXPECT_EQ(afterStatePostLoop.mPhase, static_cast(5)); + } + + TEST(MWWorldWeatherTest, masserPhasesWaxingCrescentToFirstQuarterAtCorrectTimes) + { + float dailyIncrement = 1.0f; + float speed = 0.5f; + float fadeEndAngle = 40.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 14.0f; + float fadeInFinish = 15.0f; + float fadeOutStart = 7.0f; + float fadeOutFinish = 10.0f; + float axisOffset = 35.0f; + + // Days 17 and 41, 2:57 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 17 + 2.0f + 56.0f / 60.0f); + timeStampAfter += (24.0f * 17 + 2.0f + 58.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 41 + 2.0f + 56.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 41 + 2.0f + 58.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_EQ(beforeState.mPhase, static_cast(5)); + EXPECT_EQ(afterState.mPhase, static_cast(6)); + EXPECT_EQ(beforeStatePostLoop.mPhase, static_cast(5)); + EXPECT_EQ(afterStatePostLoop.mPhase, static_cast(6)); + } + + TEST(MWWorldWeatherTest, masserPhasesFirstQuarterToWaxingGibbousAtCorrectTimes) + { + float dailyIncrement = 1.0f; + float speed = 0.5f; + float fadeEndAngle = 40.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 14.0f; + float fadeInFinish = 15.0f; + float fadeOutStart = 7.0f; + float fadeOutFinish = 10.0f; + float axisOffset = 35.0f; + + // Days 20 and 44, 5:57 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 20 + 5.0f + 56.0f / 60.0f); + timeStampAfter += (24.0f * 20 + 5.0f + 58.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 44 + 5.0f + 56.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 44 + 5.0f + 58.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_EQ(beforeState.mPhase, static_cast(6)); + EXPECT_EQ(afterState.mPhase, static_cast(7)); + EXPECT_EQ(beforeStatePostLoop.mPhase, static_cast(6)); + EXPECT_EQ(afterStatePostLoop.mPhase, static_cast(7)); + } + + TEST(MWWorldWeatherTest, masserPhasesWaxingGibbousToFullAtCorrectTimes) + { + float dailyIncrement = 1.0f; + float speed = 0.5f; + float fadeEndAngle = 40.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 14.0f; + float fadeInFinish = 15.0f; + float fadeOutStart = 7.0f; + float fadeOutFinish = 10.0f; + float axisOffset = 35.0f; + + // Days 23 and 47, 8:57 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 23 + 8.0f + 56.0f / 60.0f); + timeStampAfter += (24.0f * 23 + 8.0f + 58.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 47 + 8.0f + 56.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 47 + 8.0f + 58.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_EQ(beforeState.mPhase, static_cast(7)); + EXPECT_EQ(afterState.mPhase, static_cast(0)); + EXPECT_EQ(beforeStatePostLoop.mPhase, static_cast(7)); + EXPECT_EQ(afterStatePostLoop.mPhase, static_cast(0)); + } + + // SECUNDA PHASES + + TEST(MWWorldWeatherTest, secundaPhasesFullToWaningGibbousAtCorrectTimes) + { + float dailyIncrement = 1.2f; + float speed = 0.6f; + float fadeEndAngle = 30.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 14.0f; + float fadeInFinish = 15.0f; + float fadeOutStart = 7.0f; + float fadeOutFinish = 10.0f; + float axisOffset = 50.0f; + + // Days 2 and 26, 14:19 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 2 + 14.0f + 18.0f / 60.0f); + timeStampAfter += (24.0f * 2 + 14.0f + 20.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 26 + 14.0f + 18.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 26 + 14.0f + 20.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_EQ(beforeState.mPhase, static_cast(0)); + EXPECT_EQ(afterState.mPhase, static_cast(1)); + EXPECT_EQ(beforeStatePostLoop.mPhase, static_cast(0)); + EXPECT_EQ(afterStatePostLoop.mPhase, static_cast(1)); + } + + TEST(MWWorldWeatherTest, secundaPhasesWaningGibbousToThirdQuarterAtCorrectTimes) + { + float dailyIncrement = 1.2f; + float speed = 0.6f; + float fadeEndAngle = 30.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 14.0f; + float fadeInFinish = 15.0f; + float fadeOutStart = 7.0f; + float fadeOutFinish = 10.0f; + float axisOffset = 50.0f; + + // Days 5 and 29, 0:00 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 4 + 23.0f + 59.0f / 60.0f); + timeStampAfter += (24.0f * 5 + 0.0f + 1.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 28 + 23.0f + 59.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 29 + 0.0f + 1.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_EQ(beforeState.mPhase, static_cast(1)); + EXPECT_EQ(afterState.mPhase, static_cast(2)); + EXPECT_EQ(beforeStatePostLoop.mPhase, static_cast(1)); + EXPECT_EQ(afterStatePostLoop.mPhase, static_cast(2)); + } + + TEST(MWWorldWeatherTest, secundaPhasesThirdQuarterToWaningCrescentAtCorrectTimes) + { + float dailyIncrement = 1.2f; + float speed = 0.6f; + float fadeEndAngle = 30.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 14.0f; + float fadeInFinish = 15.0f; + float fadeOutStart = 7.0f; + float fadeOutFinish = 10.0f; + float axisOffset = 50.0f; + + // Days 8 and 32, 0:00 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 7 + 23.0f + 59.0f / 60.0f); + timeStampAfter += (24.0f * 8 + 0.0f + 1.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 31 + 23.0f + 59.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 32 + 0.0f + 1.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_EQ(beforeState.mPhase, static_cast(2)); + EXPECT_EQ(afterState.mPhase, static_cast(3)); + EXPECT_EQ(beforeStatePostLoop.mPhase, static_cast(2)); + EXPECT_EQ(afterStatePostLoop.mPhase, static_cast(3)); + } + + TEST(MWWorldWeatherTest, secundaPhasesWaningCrescentToNewAtCorrectTimes) + { + float dailyIncrement = 1.2f; + float speed = 0.6f; + float fadeEndAngle = 30.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 14.0f; + float fadeInFinish = 15.0f; + float fadeOutStart = 7.0f; + float fadeOutFinish = 10.0f; + float axisOffset = 50.0f; + + // Days 11 and 35, 0:00 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 10 + 23.0f + 59.0f / 60.0f); + timeStampAfter += (24.0f * 11 + 0.0f + 1.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 34 + 23.0f + 59.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 35 + 0.0f + 1.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_EQ(beforeState.mPhase, static_cast(3)); + EXPECT_EQ(afterState.mPhase, static_cast(4)); + EXPECT_EQ(beforeStatePostLoop.mPhase, static_cast(3)); + EXPECT_EQ(afterStatePostLoop.mPhase, static_cast(4)); + } + + TEST(MWWorldWeatherTest, secundaPhasesNewToWaxingCrescentAtCorrectTimes) + { + float dailyIncrement = 1.2f; + float speed = 0.6f; + float fadeEndAngle = 30.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 14.0f; + float fadeInFinish = 15.0f; + float fadeOutStart = 7.0f; + float fadeOutFinish = 10.0f; + float axisOffset = 50.0f; + + // Days 14 and 38, 0:00 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 13 + 23.0f + 59.0f / 60.0f); + timeStampAfter += (24.0f * 14 + 0.0f + 1.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 37 + 23.0f + 59.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 38 + 0.0f + 1.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_EQ(beforeState.mPhase, static_cast(4)); + EXPECT_EQ(afterState.mPhase, static_cast(5)); + EXPECT_EQ(beforeStatePostLoop.mPhase, static_cast(4)); + EXPECT_EQ(afterStatePostLoop.mPhase, static_cast(5)); + } + + TEST(MWWorldWeatherTest, secundaPhasesWaxingCrescentToFirstQuarterAtCorrectTimes) + { + float dailyIncrement = 1.2f; + float speed = 0.6f; + float fadeEndAngle = 30.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 14.0f; + float fadeInFinish = 15.0f; + float fadeOutStart = 7.0f; + float fadeOutFinish = 10.0f; + float axisOffset = 50.0f; + + // Days 17 and 41, 3:31 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 17 + 3.0f + 30.0f / 60.0f); + timeStampAfter += (24.0f * 17 + 3.0f + 32.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 41 + 3.0f + 30.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 41 + 3.0f + 32.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_EQ(beforeState.mPhase, static_cast(5)); + EXPECT_EQ(afterState.mPhase, static_cast(6)); + EXPECT_EQ(beforeStatePostLoop.mPhase, static_cast(5)); + EXPECT_EQ(afterStatePostLoop.mPhase, static_cast(6)); + } + + TEST(MWWorldWeatherTest, secundaPhasesFirstQuarterToWaxingGibbousAtCorrectTimes) + { + float dailyIncrement = 1.2f; + float speed = 0.6f; + float fadeEndAngle = 30.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 14.0f; + float fadeInFinish = 15.0f; + float fadeOutStart = 7.0f; + float fadeOutFinish = 10.0f; + float axisOffset = 50.0f; + + // Days 20 and 44, 7:07 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 20 + 7.0f + 6.0f / 60.0f); + timeStampAfter += (24.0f * 20 + 7.0f + 8.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 44 + 7.0f + 6.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 44 + 7.0f + 8.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_EQ(beforeState.mPhase, static_cast(6)); + EXPECT_EQ(afterState.mPhase, static_cast(7)); + EXPECT_EQ(beforeStatePostLoop.mPhase, static_cast(6)); + EXPECT_EQ(afterStatePostLoop.mPhase, static_cast(7)); + } + + TEST(MWWorldWeatherTest, secundaPhasesWaxingGibbousToFullAtCorrectTimes) + { + float dailyIncrement = 1.2f; + float speed = 0.6f; + float fadeEndAngle = 30.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 14.0f; + float fadeInFinish = 15.0f; + float fadeOutStart = 7.0f; + float fadeOutFinish = 10.0f; + float axisOffset = 50.0f; + + // Days 23 and 47, 10:43 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 23 + 10.0f + 42.0f / 60.0f); + timeStampAfter += (24.0f * 23 + 10.0f + 44.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 47 + 10.0f + 42.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 47 + 10.0f + 44.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_EQ(beforeState.mPhase, static_cast(7)); + EXPECT_EQ(afterState.mPhase, static_cast(0)); + EXPECT_EQ(beforeStatePostLoop.mPhase, static_cast(7)); + EXPECT_EQ(afterStatePostLoop.mPhase, static_cast(0)); + } + + // OFFSETS + + TEST(MWWorldWeatherTest, secundaShouldApplyIncrementOffsetAfterFirstLoop) + { + float dailyIncrement = 1.2f; + float speed = 0.6f; + float fadeEndAngle = 30.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 14.0f; + float fadeInFinish = 15.0f; + float fadeOutStart = 7.0f; + float fadeOutFinish = 10.0f; + float axisOffset = 50.0f; + + // Days 8 and 32, 3:16 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 8 + 3.0f + 15.0f / 60.0f); + timeStampAfter += (24.0f * 8 + 3.0f + 17.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 32 + 3.0f + 15.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 32 + 3.0f + 17.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_LE(beforeState.mMoonAlpha, 0.0f); + EXPECT_GT(afterState.mMoonAlpha, 0.0f); + EXPECT_LE(beforeStatePostLoop.mMoonAlpha, 0.0f); + EXPECT_GT(afterStatePostLoop.mMoonAlpha, 0.0f); + } + + TEST(MWWorldWeatherTest, moonWithLowIncrementShouldApplyIncrementOffsetAfterCycle) + { + float dailyIncrement = 0.9f; + float speed = 0.5f; + float fadeEndAngle = 40.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 0.0f; + float fadeInFinish = 0.0f; + float fadeOutStart = 0.0f; + float fadeOutFinish = 0.0f; + float axisOffset = 35.0f; + + // Days 7 and 31, 1:44 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 7 + 1.0f + 43.0f / 60.0f); + timeStampAfter += (24.0f * 7 + 1.0f + 45.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 31 + 1.0f + 43.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 31 + 1.0f + 45.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_LE(beforeState.mMoonAlpha, 0.0f); + EXPECT_GT(afterState.mMoonAlpha, 0.0f); + EXPECT_LE(beforeStatePostLoop.mMoonAlpha, 0.0f); + EXPECT_GT(afterStatePostLoop.mMoonAlpha, 0.0f); + } + + TEST(MWWorldWeatherTest, masserShouldApplyIncrementOffsetAfterCycle) + { + float dailyIncrement = 1.0f; + float speed = 0.5f; + float fadeEndAngle = 40.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 0.0f; + float fadeInFinish = 0.0f; + float fadeOutStart = 0.0f; + float fadeOutFinish = 0.0f; + float axisOffset = 35.0f; + + // Days 4 and 28, 1:02 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 4 + 1.0f + 1.0f / 60.0f); + timeStampAfter += (24.0f * 4 + 1.0f + 3.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 28 + 1.0f + 1.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 28 + 1.0f + 3.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_LE(beforeState.mMoonAlpha, 0.0f); + EXPECT_GT(afterState.mMoonAlpha, 0.0f); + EXPECT_LE(beforeStatePostLoop.mMoonAlpha, 0.0f); + EXPECT_GT(afterStatePostLoop.mMoonAlpha, 0.0f); + } + + TEST(MWWorldWeatherTest, secundaShouldApplyIncrementOffsetAfterCycle) + { + float dailyIncrement = 1.2f; + float speed = 0.6f; + float fadeEndAngle = 30.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 0.0f; + float fadeInFinish = 0.0f; + float fadeOutStart = 0.0f; + float fadeOutFinish = 0.0f; + float axisOffset = 50.0f; + + // Days 3 and 27, 2:04 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 3 + 2.0f + 3.0f / 60.0f); + timeStampAfter += (24.0f * 3 + 2.0f + 5.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 27 + 2.0f + 3.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 27 + 2.0f + 5.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_LE(beforeState.mMoonAlpha, 0.0f); + EXPECT_GT(afterState.mMoonAlpha, 0.0f); + EXPECT_LE(beforeStatePostLoop.mMoonAlpha, 0.0f); + EXPECT_GT(afterStatePostLoop.mMoonAlpha, 0.0f); + } + + TEST(MWWorldWeatherTest, moonWithIncreasedSpeedShouldApplyIncrementOffsetAfterCycle) + { + float dailyIncrement = 1.2f; + float speed = 1.6f; + float fadeEndAngle = 30.0f; + float fadeStartAngle = 50.0f; + float moonShadowEarlyFadeAngle = 0.5f; + float fadeInStart = 0.0f; + float fadeInFinish = 0.0f; + float fadeOutStart = 0.0f; + float fadeOutFinish = 0.0f; + float axisOffset = 50.0f; + + // Days 4 and 28, 1:13 + TimeStamp timeStampBefore, timeStampAfter, timeStampBeforePostLoop, timeStampAfterPostLoop; + timeStampBefore += (24.0f * 4 + 1.0f + 12.0f / 60.0f); + timeStampAfter += (24.0f * 4 + 1.0f + 14.0f / 60.0f); + timeStampBeforePostLoop += (24.0f * 28 + 1.0f + 12.0f / 60.0f); + timeStampAfterPostLoop += (24.0f * 28 + 1.0f + 14.0f / 60.0f); + + MWWorld::MoonModel moon = MWWorld::MoonModel(fadeInStart, fadeInFinish, fadeOutStart, fadeOutFinish, + axisOffset, speed, dailyIncrement, fadeStartAngle, fadeEndAngle, moonShadowEarlyFadeAngle); + + MWRender::MoonState beforeState = moon.calculateState(timeStampBefore); + MWRender::MoonState afterState = moon.calculateState(timeStampAfter); + MWRender::MoonState beforeStatePostLoop = moon.calculateState(timeStampBeforePostLoop); + MWRender::MoonState afterStatePostLoop = moon.calculateState(timeStampAfterPostLoop); + + EXPECT_LE(beforeState.mMoonAlpha, 0.0f); + EXPECT_GT(afterState.mMoonAlpha, 0.0f); + EXPECT_LE(beforeStatePostLoop.mMoonAlpha, 0.0f); + EXPECT_GT(afterStatePostLoop.mMoonAlpha, 0.0f); + } + } +}