fSunYExtreme is the game setting used to set the distance of the sun from the camera along the y-axis. Like in the More Plausible South Side Sun mod (
https://www.nexusmods.com/skyrim/mods/60937).
The sun in Skyrim is not direction based, it is position based using the xyz position from the camera. The 40 is not the total distance from the camera, it is just the distance along the y-axis.
Here is the source code to calculate the sun position that works similar to vanilla Skyrim. By default, Skyrim moves the sun along a curve that passes through these points:
sunrise - x: 400, y: 40, z: 0
midday - x: 0, y: 40, z: 400
sunset - x: -400, y: 40, z: 0
midnight - x: 0, y: 40, z: 400 (z is positive)
Code:
float* g_fSunAlphaTransTime = reinterpret_cast<float*>(0x01B117D8); // 0.5
float* g_fSunXExtreme = reinterpret_cast<float*>(0x01B117E4); // 400.0
float* g_fSunYExtreme = reinterpret_cast<float*>(0x01B117F0); // 40.0
float* g_sunriseBegin = reinterpret_cast<float*>(0x01B1175C);
float* g_sunriseEnd = reinterpret_cast<float*>(0x01B11760);
float* g_sunsetBegin = reinterpret_cast<float*>(0x01B11764);
float* g_sunsetEnd = reinterpret_cast<float*>(0x01B11768);
void CalculateSunPositionVanilla() {
Sky* sky = GetSky(); // from SKSE
Sun* sun = sky->sun;
NiNode* sunNode = reinterpret_cast<NiNode*>(sun->m_refCount); // not named correctly in SKSE
float gameHour = GetGameHour();
float sunriseTime = (*g_sunriseBegin + *g_sunriseEnd - *g_fSunAlphaTransTime)/2;
float sunsetTime = (*g_sunsetBegin + *g_sunsetEnd + *g_fSunAlphaTransTime)/2;
float percent;
if (gameHour >= sunriseTime && gameHour < sunsetTime) {
percent = 1.0f - ((gameHour-sunriseTime)/(sunsetTime-sunriseTime))*2.0f;
} else if (gameHour >= sunsetTime) {
percent = ((gameHour-sunsetTime)/(24.0f-(sunsetTime-sunriseTime)))*2.0f - 1.0f;
} else {//if (gameHour < sunriseTime) {
percent = ((gameHour+24.0f-sunsetTime)/(24.0f-(sunsetTime-sunriseTime)))*2.0f - 1.0f;
}
sunNode->m_localTransform.pos.x = *g_fSunXExtreme * percent;
sunNode->m_localTransform.pos.y = *g_fSunYExtreme;
sunNode->m_localTransform.pos.z = abs(*g_fSunXExtreme) - abs(x);
}
And here's the code that I am using to calculate the sun position using latitude and day number. It's based on the forumla from this wikipedia entry:
https://en.wikipedia.org/wiki/Solar_azimuth_angleCode:
void CalculateSunPosition(int dayNumber, float latitude) {
Sky* sky = GetSky(); // from SKSE
Sun* sun = sky->sun;
NiNode* sunNode = reinterpret_cast<NiNode*>(sun->m_refCount); // not named correctly in SKSE
float gameHour = GetGameHour();
float solarZenith = 0.0f;
float solarAzimuth = 0.0f;
float axis = 23.439f;
float JanDayNumber = dayNumber - 11.0f;
if (JanDayNumber > 365.0f) JanDayNumber -= 365.0f;
if (JanDayNumber < 1) JanDayNumber += 365f;
latitude = toRadians(latitude);
float sinLatitude = sin(latitude);
float cosLatitude = cos(latitude);
// Calculate declination
float declination = -toRadians(axis) * cos(toRadians((360.0f / 365.0f) * (JanDayNumber + 10.0f)));
float sinDeclination = sin(declination);
float cosDeclination = cos(declination);
// Calculate hour angle
float hourAngle = toRadians(15.0f*(gameHour-12.0f));
float cosHourAngle = cos(hourAngle);
// Calculate solar zenith
solarZenith = acos(sinLatitude*sinDeclination + cosLatitude*cosDeclination*cosHourAngle);
float sinSolarZenith = sin(solarZenith);
float cosSolarZenith = cos(solarZenith);
// Calculate solar azimuth
solarAzimuth = acos((sinDeclination*cosLatitude - cosHourAngle*cosDeclination*sinLatitude)/sinSolarZenith);
if (hourAngle > 0) solarAzimuth = -solarAzimuth;
solarAzimuth = -solarAzimuth + PI/2; // Convert to Skyrim coordinate system
float sinSolarAzimuth = sin(solarAzimuth);
float cosSolarAzimuth = cos(solarAzimuth);
// Convert to xyz coordinates
float r = *g_fSunXExtreme;
sunNode->m_localTransform.pos.x = r * sinSolarZenith * cosSolarAzimuth;
sunNode->m_localTransform.pos.y = r * sinSolarZenith * sinSolarAzimuth;
sunNode->m_localTransform.pos.z = r * cosSolarZenith;
}