using System;
using System.Collections.Generic;
namespace UnityEngine.AzureSky
{
/// Class that handles the time, date and location stuff.
[Serializable]
public sealed class AzureTimeSystem
{
/// The transform that will represent the position of the sun in the sky.
public Transform sunTransform { get => m_sunTransform; set => m_sunTransform = value; }
[SerializeField] private Transform m_sunTransform = null;
/// Stores the sun elevation in the sky in the Range(-1.0, 1.0).
public float sunElevation => m_sunElevation;
private float m_sunElevation = 0.0f;
/// The sun elevation converted from Range(-1.0, 1.0) to Range(0.0, 1.0).
private float m_sunElevationTime = 0.0f;
/// The transform that will represent the position of the moon in the sky.
public Transform moonTransform { get => m_moonTransform; set => m_moonTransform = value; }
[SerializeField] private Transform m_moonTransform = null;
/// Stores the moon elevation in the sky in the Range(-1.0, 1.0).
public float moonElevation => m_moonElevation;
private float m_moonElevation = 0.0f;
/// The transform that will represent the position of the starfield in the sky.
public Transform starfieldTransform { get => m_starfieldTransform; set => m_starfieldTransform = value; }
[SerializeField] private Transform m_starfieldTransform = null;
/// The directional light that will apply the sun and moon lighting to the scene.
public Transform directionalLight { get => m_directionalLight; set => m_directionalLight = value; }
[SerializeField] private Transform m_directionalLight = null;
/// Used internally to compute the directional light direction.
private Vector3 m_directionalLightDirection = Vector3.zero;
/// The time mode used to perform position of the sun and moon in the sky according to the time of day.
public AzureTimeMode timeMode { get => m_timeMode; set => m_timeMode = value; }
[SerializeField] private AzureTimeMode m_timeMode = AzureTimeMode.Simple;
/// The direction in which the time of day will flow.
public AzureTimeDirection timeDirection { get => m_timeDirection; set => m_timeDirection = value; }
[SerializeField] private AzureTimeDirection m_timeDirection = AzureTimeDirection.Forward;
/// The time repeat mode cycle.
public AzureTimeLoop timeLoop { get => m_timeLoop; set => m_timeLoop = value; }
[SerializeField] private AzureTimeLoop m_timeLoop = AzureTimeLoop.Off;
///
/// The timeline that represents the day cycle.
/// Do not confuse with the time of day, they may not be the same thing depending on the configuration.
///
public float timeline { get => m_timeline; set { m_timeline = value; OnTimelineChanged(); } }
[SerializeField] private float m_timeline = 6.5f;
/// The north-south angle of a position on the Earth's surface.
public float latitude { get => m_latitude; set => m_latitude = value; }
[SerializeField] private float m_latitude = 0f;
/// The east-west angle of a position on the Earth's surface.
public float longitude { get => m_longitude; set => m_longitude = value; }
[SerializeField] private float m_longitude = 0f;
///
/// Prior to 1972, this time was called Greenwich Mean Time (GMT).
/// But is now referred to as Coordinated Universal Time or Universal Time Coordinated (UTC).
///
public float utc { get => m_utc; set => m_utc = value; }
[SerializeField] private float m_utc = 0;
/// Represents the day in the calendar.
public int day
{
get => m_day;
set
{
m_day = value;
UpdateCalendar();
#if UNITY_EDITOR
AzureNotificationCenterEditor.Invoke.RequestCalendarUpdateCallback();
#endif
}
}
[SerializeField] private int m_day = 1;
/// Represents the month in the calendar.
public int month
{
get => m_month;
set
{
m_month = value;
UpdateCalendar();
#if UNITY_EDITOR
AzureNotificationCenterEditor.Invoke.RequestCalendarUpdateCallback();
#endif
}
}
[SerializeField] private int m_month = 1;
/// Represents the year in the calendar.
public int year
{
get => m_year;
set
{
m_year = value;
UpdateCalendar();
#if UNITY_EDITOR
AzureNotificationCenterEditor.Invoke.RequestCalendarUpdateCallback();
#endif
}
}
[SerializeField] private int m_year = 2024;
/// The value the timeline should start the scene when entering the play mode.
public float startTime { get => m_startTime; set => m_startTime = value; }
[SerializeField] private float m_startTime = 6.5f;
/// The duration of the day cycle in minutes.
public float dayLength { get => m_dayLength; set { m_dayLength = value; ComputeTimeProgressionStep(); } }
[SerializeField] private float m_dayLength = 24.0f;
/// The minimmun directional light angle according to the horizon line.
/// Useful for saving performance by avoiding stretched shadows when the light is horizontally tilted relative to the scene.
public float minLightAltitude { get => m_minLightAltitude; set => m_minLightAltitude = value; }
[SerializeField] private float m_minLightAltitude = 0.0f;
/// The time that marks the transition from nighttime to sunrise in the timeline cycle.
public float dawnTime
{
get => m_dawnTime;
set
{
m_dawnTime = value;
UpdateTimeLengthCurve();
}
}
[SerializeField] private float m_dawnTime = 6f;
/// The time that marks the transition from sunset to nighttime in the timeline cycle.
public float duskTime
{
get => m_duskTime;
set
{
m_duskTime = value;
UpdateTimeLengthCurve();
}
}
[SerializeField] private float m_duskTime = 18.0f;
/// The current time of day inside the timeline cycle.
public float timeOfDay => m_timeOfDay;
[SerializeField] private float m_timeOfDay = 6.5f;
/// The hour according to the time of day.
public int hour => m_hour;
[SerializeField] private int m_hour = 6;
/// The minute according to the time of day.
public int minute => m_minute;
[SerializeField] private int m_minute = 0;
/// The time used to evaluate the curves and gradients.
public float evaluationTime => m_evaluationTime;
[SerializeField] private float m_evaluationTime = 6.5f;
/// The curve used to evaluate the daytime and nightime length.
public AnimationCurve timeLengthCurve { get => m_timeLengthCurve; set => m_timeLengthCurve = value; }
[SerializeField] private AnimationCurve m_timeLengthCurve = AnimationCurve.Linear(0f, 0f, 24f, 24f);
/// The list of the celistial bodies the system can simulate other than the sun and moon.
public List celestialBodiesList { get => m_celestialBodiesList; set => m_celestialBodiesList = value; }
[SerializeField] private List m_celestialBodiesList = new List();
/// String array that stores the name of each day in the week.
public string[] weekNameList => m_weekNameList;
private readonly string[] m_weekNameList = new string[]
{
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
};
/// String array that stores the name of each month.
public string[] monthNameList => m_monthNameList;
private readonly string[] m_monthNameList = new string[]
{
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
};
/// Array with 42 numeric strings used to fill a calendar.
public string[] DayNumberList { get => m_dayNumberList; set => m_dayNumberList = value; }
[SerializeField] private string[] m_dayNumberList = new string[]
{
"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10",
"11", "12", "13", "14", "15", "16", "17", "18", "19", "20",
"21", "22", "23", "24", "25", "26", "27", "28", "29", "30",
"31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41"
};
/// The current selected day in the calendar.
public int selectedCalendarDay => m_selectedCalendarDay;
[SerializeField] private int m_selectedCalendarDay = 1;
/// The number that represents the current selected day in the week (0 - 6).
public int dayOfWeek => m_dayOfWeek;
private int m_dayOfWeek = 0;
/// Stores the total number of days in the current month.
public int daysInMonth => m_daysInMonth;
private int m_daysInMonth = 30;
/// Used internally to update the calendar.
private DateTime m_dateTime;
/// Used internally to stores the total number of days in the previous month, usefull when the Time Direction is defined to Backward.
private int m_daysInPreviousMonth = 30;
/// Used internally to stores the previous month in the calendar, usefull when the Time Direction is defined to Backward.
private int m_previousMonth = 1;
/// The time progression step used to move the time of day.
private float m_timeProgressionStep = 0f;
/// Used internally to trigger the OnMinuteChange event.
private int m_previousMinute = 0;
/// Used internally to trigger the OnHourChange event.
private int m_previousHour = 6;
/// The exactly Time.time the timeline transition started.
private float m_timelineTransitionStartTime = 0.0f;
/// The start timeline point where a timeline transition should starte.
private float m_timelineTransitionStartPoint = 0.0f;
/// The end timeline point where a timeline transition should end.
private float m_timelineTransitionEndPoint = 0.0f;
/// The duration in seconds of the timeline transition.
private float m_timelineTransitionDuration = 0.0f;
/// The smooth step of the timeline transition.
private float m_timelineTransitionStep = 0.0f;
/// The temporary timeline value used by the timeline transition.
private float m_temporaryTimeline = 0.0f;
/// Is a timeline transition current running?
public bool isPlayingTimelineTransition => m_isPlayingTimelineTransition;
private bool m_isPlayingTimelineTransition = false;
/// Called just before the first Update() of the AzureCoreSystem script.
public void Start()
{
if (Application.isPlaying)
{
m_timeline = m_startTime;
}
// Get the correct time of day from the timeline cycle
m_timeOfDay = m_timeLengthCurve.Evaluate(m_timeline);
// Convert the time of day to hour and minute
m_hour = (int)Mathf.Floor(m_timeOfDay);
m_minute = (int)Mathf.Floor(m_timeOfDay * 60 % 60);
m_previousMinute = m_minute;
m_previousHour = m_hour;
ComputeEvaluationTime();
UpdateTimeLengthCurve();
ComputeTimeProgressionStep();
// First update of the calendar to sync with the start time and date
UpdateCalendar();
// First update of the sun, moon and directional light transforms
UpdateCelestialBodies();
}
///
/// Moves the time of day to make all the time dependent things work.
/// These calculations need to be done every frame.
///
public void UpdateTimeOfDay()
{
if (!m_isPlayingTimelineTransition)
{
if (m_dayLength > 0)
{
// Moves the timeline forward
if (m_timeDirection == AzureTimeDirection.Forward)
{
m_timeline += m_timeProgressionStep * Time.deltaTime;
// Change to the next day in the calendar
if (m_timeline > 24.0f)
{
IncreaseDay();
m_timeline = 0.0f;
}
}
// Moves the timeline backward
else
{
m_timeline -= m_timeProgressionStep * Time.deltaTime;
// Change to the previous day in the calendar
if (m_timeline < 0.0f)
{
DecreaseDay();
m_timeline = 24.0f;
}
}
OnTimelineChanged();
}
}
else
{
ApplyTimelineTransition();
OnTimelineChanged();
}
}
#if UNITY_EDITOR
/// Performs calculations that are required in edit mode only.
public void OnEditorUpdate()
{
// Get the correct time of day from the timeline cycle
m_timeOfDay = m_timeLengthCurve.Evaluate(m_timeline);
// Convert the time of day to hours and minutes
m_hour = (int)Mathf.Floor(m_timeOfDay);
m_minute = (int)Mathf.Floor(m_timeOfDay * 60 % 60);
UpdateCelestialBodies();
ComputeEvaluationTime();
}
#endif
/// Computes the time progression step according to the day length value.
private void ComputeTimeProgressionStep()
{
if (m_dayLength > 0.0f)
{
m_timeProgressionStep = (24.0f / 60.0f) / m_dayLength;
}
else
{
m_timeProgressionStep = 0.0f;
}
}
/// Updates the transforms of the sun, moon and directional light according to the time of day.
public void UpdateCelestialBodies()
{
// Compute the direction of the transforms
if (m_timeMode == AzureTimeMode.Simple)
{
m_sunTransform.rotation = Quaternion.Euler(0.0f, m_longitude, -m_latitude) * Quaternion.Euler(((m_timeOfDay - m_utc) * 360.0f / 24.0f) - 90.0f, 180.0f, 0.0f);
m_moonTransform.rotation = m_sunTransform.rotation * Quaternion.Euler(0.0f, -180.0f, 0.0f);
m_starfieldTransform.rotation = m_sunTransform.rotation;
}
else
{
// Initializations
float hour = m_timeOfDay - m_utc;
float rad = Mathf.Deg2Rad;
float deg = Mathf.Rad2Deg;
float latitude = m_latitude * rad;
// The time scale
float d = 367 * m_year - 7 * (m_year + (m_month + 9) / 12) / 4 + 275 * m_month / 9 + m_day - 730530;
d = d + (hour / 24.0f);
// Obliquity of the ecliptic: The tilt of earth's axis of rotation
float ecl = 23.4393f - 3.563E-7f * d;
ecl *= rad;
// Orbital elements of the sun
float N = 0.0f;
float i = 0.0f;
float w = 282.9404f + 4.70935E-5f * d;
float a = 1.000000f;
float e = 0.016709f - 1.151E-9f * d;
float M = 356.0470f + 0.9856002585f * d;
// Eccentric anomaly
M *= rad;
float E = M + e * Mathf.Sin(M) * (1f + e * Mathf.Cos(M));
// Sun's distance (r) and its true anomaly (v)
float xv = Mathf.Cos(E) - e;
float yv = Mathf.Sqrt(1.0f - (e * e)) * Mathf.Sin(E);
float v = Mathf.Atan2(yv, xv) * deg;
float r = Mathf.Sqrt((xv * xv) + (yv * yv));
// Sun's true longitude
float lonsun = (v + w) * rad;
// Convert lonsun,r to ecliptic rectangular geocentric coordinates xs,ys:
float xs = r * Mathf.Cos(lonsun);
float ys = r * Mathf.Sin(lonsun);
// zs = 0;
// To convert this to equatorial, rectangular, geocentric coordinates, compute:
float xe = xs;
float ye = ys * Mathf.Cos(ecl);
float ze = ys * Mathf.Sin(ecl);
// Sun's right ascension (RA) and declination (Decl):
float RA = Mathf.Atan2(ye, xe);
float Decl = Mathf.Atan2(ze, Mathf.Sqrt((xe * xe) + (ye * ye)));
// The sidereal time
float Ls = v + w;
float GMST0 = Ls + 180.0f;
float GMST = GMST0 + (hour * 15.0f);
float LST = (GMST + m_longitude) * rad;
// Azimuthal coordinates
float HA = LST - RA;
float x = Mathf.Cos(HA) * Mathf.Cos(Decl);
float y = Mathf.Sin(HA) * Mathf.Cos(Decl);
float z = Mathf.Sin(Decl);
float xhor = (x * Mathf.Sin(latitude)) - (z * Mathf.Cos(latitude));
float yhor = y;
float zhor = (x * Mathf.Cos(latitude)) + (z * Mathf.Sin(latitude));
float azimuth = Mathf.Atan2(yhor, xhor);
float altitude = Mathf.Asin(zhor);
// Gets the celestial rotation
Vector3 celestialRotation;
celestialRotation.x = altitude * deg;
celestialRotation.y = azimuth * deg;
celestialRotation.z = 0.0f;
//m_sunTransform.rotation = Quaternion.Euler(celestialRotation);
m_starfieldTransform.rotation = Quaternion.Euler(90.0f - m_latitude, 0.0f, 0.0f) * Quaternion.Euler(0.0f, m_longitude, 0.0f) * Quaternion.Euler(0.0f, LST * deg, 0.0f);
m_sunTransform.position = Quaternion.Euler(celestialRotation) * new Vector3(0.0f, 0.0f, 1.0f);
m_sunTransform.rotation = Quaternion.LookRotation(m_sunTransform.position, m_starfieldTransform.up);
// Orbital elements of the Moon
N = 125.1228f - 0.0529538083f * d;
i = 5.1454f;
w = 318.0634f + 0.1643573223f * d;
//a = 0.002566882112227f; (AU)
a = 60.2666f; // Earth radius
e = 0.054900f;
M = 115.3654f + 13.0649929509f * d;
// Eccentric anomaly
M *= rad;
E = M + e * Mathf.Sin(M) * (1f + e * Mathf.Cos(M));
// Moon's distance and true anomaly
xv = a * (Mathf.Cos(E) - e);
yv = a * (Mathf.Sqrt(1f - e * e) * Mathf.Sin(E));
v = Mathf.Atan2(yv, xv) * deg;
r = Mathf.Sqrt(xv * xv + yv * yv);
// Moon's true longitude
lonsun = (v + w) * rad;
float sinLongitude = Mathf.Sin(lonsun);
float cosLongitude = Mathf.Cos(lonsun);
// The position in space - for the planets
// Geocentric (Earth-centered) coordinates - for the moon
N *= rad;
i *= rad;
float xh = r * (Mathf.Cos(N) * cosLongitude - Mathf.Sin(N) * sinLongitude * Mathf.Cos(i));
float yh = r * (Mathf.Sin(N) * cosLongitude + Mathf.Cos(N) * sinLongitude * Mathf.Cos(i));
float zh = r * (sinLongitude * Mathf.Sin(i));
// Geocentric (Earth-centered) coordinates
// For the moon this is the same as the position in space, there is no need to calculate again
// float xg = xh;
// float yg = yh;
// float zg = zh;
// Equatorial coordinates
xe = xh;
ye = yh * Mathf.Cos(ecl) - zh * Mathf.Sin(ecl);
ze = yh * Mathf.Sin(ecl) + zh * Mathf.Cos(ecl);
// Moon's right ascension (RA) and declination (Decl)
RA = Mathf.Atan2(ye, xe);
Decl = Mathf.Atan2(ze, Mathf.Sqrt(xe * xe + ye * ye));
// The sidereal time
// It is already calculated for the sun, there is no need to calculate again
// Azimuthal coordinates
HA = LST - RA;
x = Mathf.Cos(HA) * Mathf.Cos(Decl);
y = Mathf.Sin(HA) * Mathf.Cos(Decl);
z = Mathf.Sin(Decl);
xhor = x * Mathf.Sin(latitude) - z * Mathf.Cos(latitude);
yhor = y;
zhor = x * Mathf.Cos(latitude) + z * Mathf.Sin(latitude);
azimuth = Mathf.Atan2(yhor, xhor);
altitude = Mathf.Asin(zhor);
// Gets the celestial rotation
celestialRotation.x = altitude * deg;
celestialRotation.y = azimuth * deg;
celestialRotation.z = 0.0f;
//m_moonTransform.localRotation = Quaternion.Euler(celestialRotation);
//m_moonDistance = r * 6371f;
m_moonTransform.position = Quaternion.Euler(celestialRotation) * new Vector3(0.0f, 0.0f, 1.0f);
m_moonTransform.rotation = Quaternion.LookRotation(m_moonTransform.position, m_starfieldTransform.up);
// Planets
if (m_celestialBodiesList.Count > 0)
{
AzureCelestialBody celestialBody;
for (int index = 0; index < m_celestialBodiesList.Count; index++)
{
celestialBody = m_celestialBodiesList[index];
if (!celestialBody.transform) continue;
// Orbital elements of the planets
switch (celestialBody.type)
{
case AzureCelestialBodyType.Mercury:
N = 48.3313f + 3.24587E-5f * d;
i = 7.0047f + 5.00E-8f * d;
w = 29.1241f + 1.01444E-5f * d;
a = 0.387098f;
e = 0.205635f + 5.59E-10f * d;
M = 168.6562f + 4.0923344368f * d;
break;
case AzureCelestialBodyType.Venus:
N = 76.6799f + 2.46590E-5f * d;
i = 3.3946f + 2.75E-8f * d;
w = 54.8910f + 1.38374E-5f * d;
a = 0.723330f;
e = 0.006773f - 1.302E-9f * d;
M = 48.0052f + 1.6021302244f * d;
break;
case AzureCelestialBodyType.Mars:
N = 49.5574f + 2.11081E-5f * d;
i = 1.8497f - 1.78E-8f * d;
w = 286.5016f + 2.92961E-5f * d;
a = 1.523688f;
e = 0.093405f + 2.516E-9f * d;
M = 18.6021f + 0.5240207766f * d;
break;
case AzureCelestialBodyType.Jupiter:
N = 100.4542f + 2.76854E-5f * d;
i = 1.3030f - 1.557E-7f * d;
w = 273.8777f + 1.64505E-5f * d;
a = 5.20256f;
e = 0.048498f + 4.469E-9f * d;
M = 19.8950f + 0.0830853001f * d;
break;
case AzureCelestialBodyType.Saturn:
N = 113.6634f + 2.38980E-5f * d;
i = 2.4886f - 1.081E-7f * d;
w = 339.3939f + 2.97661E-5f * d;
a = 9.55475f;
e = 0.055546f - 9.499E-9f * d;
M = 316.9670f + 0.0334442282f * d;
break;
case AzureCelestialBodyType.Uranus:
N = 74.0005f + 1.3978E-5f * d;
i = 0.7733f + 1.9E-8f * d;
w = 96.6612f + 3.0565E-5f * d;
a = 19.18171f - 1.55E-8f * d;
e = 0.047318f + 7.45E-9f * d;
M = 142.5905f + 0.011725806f * d;
break;
case AzureCelestialBodyType.Neptune:
N = 131.7806f + 3.0173E-5f * d;
i = 1.7700f - 2.55E-7f * d;
w = 272.8461f - 6.027E-6f * d;
a = 30.05826f + 3.313E-8f * d;
e = 0.008606f + 2.15E-9f * d;
M = 260.2471f + 0.005995147f * d;
break;
// No analytical theory has ever been constructed for the planet Pluto.
// Our most accurate representation of the motion of this planet is from numerical integrations.
case AzureCelestialBodyType.Pluto:
float S = 50.03f + 0.033459652f * d;
float P = 238.95f + 0.003968789f * d;
S *= rad;
P *= rad;
float pluto_lonecl = 238.9508f + 0.00400703f * d
- 19.799f * Mathf.Sin(P) + 19.848f * Mathf.Cos(P)
+ 0.897f * Mathf.Sin(2 * P) - 4.956f * Mathf.Cos(2 * P)
+ 0.610f * Mathf.Sin(3 * P) + 1.211f * Mathf.Cos(3 * P)
- 0.341f * Mathf.Sin(4 * P) - 0.190f * Mathf.Cos(4 * P)
+ 0.128f * Mathf.Sin(5 * P) - 0.034f * Mathf.Cos(5 * P)
- 0.038f * Mathf.Sin(6 * P) + 0.031f * Mathf.Cos(6 * P)
+ 0.020f * Mathf.Sin(S - P) - 0.010f * Mathf.Cos(S - P);
float pluto_latecl = -3.9082f
- 5.453f * Mathf.Sin(P) - 14.975f * Mathf.Cos(P)
+ 3.527f * Mathf.Sin(2 * P) + 1.673f * Mathf.Cos(2 * P)
- 1.051f * Mathf.Sin(3 * P) + 0.328f * Mathf.Cos(3 * P)
+ 0.179f * Mathf.Sin(4 * P) - 0.292f * Mathf.Cos(4 * P)
+ 0.019f * Mathf.Sin(5 * P) + 0.100f * Mathf.Cos(5 * P)
- 0.031f * Mathf.Sin(6 * P) - 0.026f * Mathf.Cos(6 * P)
+ 0.011f * Mathf.Cos(S - P);
float pluto_r = 40.72f
+ 6.68f * Mathf.Sin(P) + 6.90f * Mathf.Cos(P)
- 1.18f * Mathf.Sin(2 * P) - 0.03f * Mathf.Cos(2 * P)
+ 0.15f * Mathf.Sin(3 * P) - 0.14f * Mathf.Cos(3 * P);
// Geocentric (Earth-centered) coordinates
pluto_lonecl *= rad;
pluto_latecl *= rad;
float pluto_cosLatecl = Mathf.Cos(pluto_latecl);
xh = pluto_r * Mathf.Cos(pluto_lonecl) * pluto_cosLatecl;
yh = pluto_r * Mathf.Sin(pluto_lonecl) * pluto_cosLatecl;
zh = pluto_r * Mathf.Sin(pluto_latecl);
// From the sun computation
// xs = r * cosLongitude;
// ys = r * sinLongitude;
float pluto_xg = xh + xs;
float pluto_yg = yh + ys;
float pluto_zg = zh;
// Equatorial coordinates
xe = pluto_xg;
ye = pluto_yg * Mathf.Cos(ecl) - pluto_zg * Mathf.Sin(ecl);
ze = pluto_yg * Mathf.Sin(ecl) + pluto_zg * Mathf.Cos(ecl);
// Moon's right ascension (RA) and declination (Decl)
RA = Mathf.Atan2(ye, xe);
Decl = Mathf.Atan2(ze, Mathf.Sqrt(xe * xe + ye * ye));
// The sidereal time
// It is already calculated for the sun, there is no need to calculate again
// Azimuthal coordinates
HA = LST - RA;
x = Mathf.Cos(HA) * Mathf.Cos(Decl);
y = Mathf.Sin(HA) * Mathf.Cos(Decl);
z = Mathf.Sin(Decl);
xhor = x * Mathf.Sin(latitude) - z * Mathf.Cos(latitude);
yhor = y;
zhor = x * Mathf.Cos(latitude) + z * Mathf.Sin(latitude);
azimuth = Mathf.Atan2(yhor, xhor);
altitude = Mathf.Asin(zhor);
// Gets the celestial rotation
celestialRotation.x = altitude * deg;
celestialRotation.y = azimuth * deg;
celestialRotation.z = 0.0f;
//celestialBody.transform.rotation = Quaternion.Euler(celestialRotation);
celestialBody.transform.position = Quaternion.Euler(celestialRotation) * new Vector3(0.0f, 0.0f, 1.0f);
celestialBody.transform.rotation = Quaternion.LookRotation(celestialBody.transform.position, m_starfieldTransform.up);
continue;
}
// Eccentric anomaly
M *= rad;
E = M + e * Mathf.Sin(M) * (1f + e * Mathf.Cos(M));
// Planet's distance and true anomaly
xv = a * (Mathf.Cos(E) - e);
yv = a * (Mathf.Sqrt(1f - e * e) * Mathf.Sin(E));
v = Mathf.Atan2(yv, xv) * deg;
r = Mathf.Sqrt(xv * xv + yv * yv);
// Planet's true longitude
lonsun = (v + w) * rad;
cosLongitude = Mathf.Cos(lonsun);
sinLongitude = Mathf.Sin(lonsun);
// The position in space - heliocentric (Sun-centered) position
N *= rad;
i *= rad;
xh = r * (Mathf.Cos(N) * cosLongitude - Mathf.Sin(N) * sinLongitude * Mathf.Cos(i));
yh = r * (Mathf.Sin(N) * cosLongitude + Mathf.Cos(N) * sinLongitude * Mathf.Cos(i));
zh = r * (sinLongitude * Mathf.Sin(i));
float lonecl = Mathf.Atan2(yh, xh);
float latecl = Mathf.Atan2(zh, Mathf.Sqrt(xh * xh + yh * yh));
// Geocentric (Earth-centered) coordinates
float cosLatecl = Mathf.Cos(latecl);
xh = r * Mathf.Cos(lonecl) * cosLatecl;
yh = r * Mathf.Sin(lonecl) * cosLatecl;
zh = r * Mathf.Sin(latecl);
// From the sun computation
// xs = r * cosLongitude;
// ys = r * sinLongitude;
float xg = xh + xs;
float yg = yh + ys;
float zg = zh;
// Equatorial coordinates
xe = xg;
ye = yg * Mathf.Cos(ecl) - zg * Mathf.Sin(ecl);
ze = yg * Mathf.Sin(ecl) + zg * Mathf.Cos(ecl);
// Planet's right ascension (RA) and declination (Decl)
RA = Mathf.Atan2(ye, xe);
Decl = Mathf.Atan2(ze, Mathf.Sqrt(xe * xe + ye * ye));
// The sidereal time
// It is already calculated for the sun, there is no need to calculate again
// Azimuthal coordinates
HA = LST - RA;
x = Mathf.Cos(HA) * Mathf.Cos(Decl);
y = Mathf.Sin(HA) * Mathf.Cos(Decl);
z = Mathf.Sin(Decl);
xhor = x * Mathf.Sin(latitude) - z * Mathf.Cos(latitude);
yhor = y;
zhor = x * Mathf.Cos(latitude) + z * Mathf.Sin(latitude);
azimuth = Mathf.Atan2(yhor, xhor);
altitude = Mathf.Asin(zhor);
// Gets the celestial rotation
celestialRotation.x = altitude * deg;
celestialRotation.y = azimuth * deg;
celestialRotation.z = 0.0f;
//celestialBody.transform.rotation = Quaternion.Euler(celestialRotation);
celestialBody.transform.position = Quaternion.Euler(celestialRotation) * new Vector3(0.0f, 0.0f, 1.0f);
celestialBody.transform.rotation = Quaternion.LookRotation(celestialBody.transform.position, m_starfieldTransform.up);
}
}
}
// Computes sun and moon elevation
m_sunElevation = Vector3.Dot(-m_sunTransform.forward, Vector3.up);
m_moonElevation = Vector3.Dot(-m_moonTransform.forward, Vector3.up);
// Set the directional light direction
m_directionalLight.localRotation = Quaternion.LookRotation(m_sunElevation >= 0.0f ? m_sunTransform.forward : m_moonTransform.forward);
// Avoid the directional light to get close to the horizon line
m_directionalLightDirection = m_directionalLight.localEulerAngles;
if (m_directionalLightDirection.x <= m_minLightAltitude) { m_directionalLightDirection.x = m_minLightAltitude; }
m_directionalLight.localEulerAngles = m_directionalLightDirection;
}
/// Increases a day in the calendar.
public void IncreaseDay()
{
if (m_timeLoop != AzureTimeLoop.Dayly)
{
m_day++;
if (m_day > m_daysInMonth)
{
m_day = 1;
IncreaseMonth();
AzureNotificationCenter.Invoke.OnDayChangedCallback(this);
return;
}
else
{
AzureNotificationCenter.Invoke.OnDayChangedCallback(this);
}
}
UpdateCalendar();
}
/// Decreases a day in the calendar.
public void DecreaseDay()
{
if (m_timeLoop != AzureTimeLoop.Dayly)
{
m_day--;
m_previousMonth = m_timeLoop == AzureTimeLoop.Monthly ? m_month : m_month - 1;
if (m_previousMonth < 1) m_previousMonth = 12;
m_daysInPreviousMonth = DateTime.DaysInMonth(m_year, m_previousMonth);
if (m_day < 1)
{
m_day = m_daysInPreviousMonth;
DecreaseMonth();
AzureNotificationCenter.Invoke.OnDayChangedCallback(this);
return;
}
else
{
AzureNotificationCenter.Invoke.OnDayChangedCallback(this);
}
}
UpdateCalendar();
}
/// Increases a month in the calendar.
public void IncreaseMonth()
{
if (m_timeLoop != AzureTimeLoop.Monthly)
{
m_month++;
if (m_month > 12)
{
m_month = 1;
IncreaseYear();
AzureNotificationCenter.Invoke.OnMonthChangedCallback(this);
return;
}
else
{
AzureNotificationCenter.Invoke.OnMonthChangedCallback(this);
}
}
UpdateCalendar();
}
/// Decreases a month in the calendar.
public void DecreaseMonth()
{
if (m_timeLoop != AzureTimeLoop.Monthly)
{
m_month--;
if (m_month < 1)
{
m_month = 12;
DecreaseYear();
AzureNotificationCenter.Invoke.OnMonthChangedCallback(this);
return;
}
else
{
AzureNotificationCenter.Invoke.OnMonthChangedCallback(this);
}
}
UpdateCalendar();
}
/// Increases a year in the calendar.
public void IncreaseYear()
{
if (m_timeLoop != AzureTimeLoop.Yearly)
{
m_year++;
if (m_year > 9999) m_year = 0;
AzureNotificationCenter.Invoke.OnYearChangedCallback(this);
}
UpdateCalendar();
}
/// Decreases a year in the calendar.
public void DecreaseYear()
{
if (m_timeLoop != AzureTimeLoop.Yearly)
{
m_year--;
if (m_year < 0) m_year = 9999;
AzureNotificationCenter.Invoke.OnYearChangedCallback(this);
}
UpdateCalendar();
}
/// The proper way for setting a custom date.
public void SetDate(int day, int month, int year)
{
m_year = year;
m_month = month;
m_day = day;
UpdateCalendar();
#if UNITY_EDITOR
AzureNotificationCenterEditor.Invoke.RequestCalendarUpdateCallback();
#endif
}
/// Returns the current time of day as a Vector2Int(hours, minutes).
public Vector2Int GetTimeOfDayVector()
{
return new Vector2Int(m_hour, m_minute);
}
/// Returns the current time of day as a string ("00:00").
public string GetTimeOfDayString()
{
return hour.ToString("00") + ":" + minute.ToString("00");
}
/// Returns the current date as a Vector3Int(year, month, day).
public Vector3Int GetDateVector()
{
return new Vector3Int(m_year, m_month, m_day);
}
/// Returns the current date converted to string using the default date format used by Azure ("January 01, 2024").
public string GetDateString()
{
// Format: "MMMM dd, yyyy"
return m_monthNameList[m_month - 1] + " " + m_day.ToString("00") + ", " + m_year.ToString("0000");
}
/// Returns the current date converted to string using a custom format as parameter.
public string GetDateString(string format)
{
m_dateTime = new DateTime(m_year, m_month, m_day);
return m_dateTime.ToString(format);
}
/// Returns the current day of the week as string.
public string GetDayOfWeekString()
{
m_dateTime = new DateTime(m_year, m_month, m_day);
return m_weekNameList[(int)m_dateTime.DayOfWeek];
}
/// Returns the current day of the week as an integer number between 0 and 6.
public int GetDayOfWeekInteger()
{
DateTime dateTime = new DateTime(m_year, m_month, m_day);
return (int)dateTime.DayOfWeek;
}
/// Adjust the calendar when there is a change in the date.
public void UpdateCalendar()
{
// Avoid selecting a date that does not exist
m_year = Mathf.Clamp(year, 0, 9999);
m_month = Mathf.Clamp(month, 1, 12);
m_daysInMonth = DateTime.DaysInMonth(m_year, m_month);
m_day = Mathf.Clamp(day, 1, m_daysInMonth);
// Creates a custom DateTime at the first day of the current month
m_dateTime = new DateTime(m_year, m_month, 1);
// Gets the day of week corresponding to this custom DateTime
m_dayOfWeek = (int)m_dateTime.DayOfWeek;
// Keeps the same day selected in the calendar even when the date is changed externally
m_selectedCalendarDay = m_day - 1 + m_dayOfWeek;
for (int i = 0; i < m_dayNumberList.Length; i++)
{
// Set the strings of all calendar's spots to empty
if (i < m_dayOfWeek || i >= (m_dayOfWeek + m_daysInMonth))
{
m_dayNumberList[i] = "";
continue;
}
// Sets the day number only to the valid calendar's spots of the current month in use by the calendar
m_dateTime = new DateTime(m_year, m_month, (i - m_dayOfWeek) + 1);
m_dayNumberList[i] = m_dateTime.Day.ToString();
}
}
/// Starts a timeline transition to a desired time.
public void StartTimelineTransition(float targetTime, float transitionTime)
{
Mathf.Clamp(targetTime, 0.0f, 24.0f);
m_timelineTransitionStartTime = Time.time;
m_timelineTransitionStartPoint = m_timeline;
m_timelineTransitionDuration = transitionTime;
if (m_timeDirection == AzureTimeDirection.Forward)
{
if (targetTime < m_timeline)
{
m_timelineTransitionEndPoint = m_timeline + (targetTime - m_timeline + 24f);
}
else { m_timelineTransitionEndPoint = targetTime; }
}
else
{
if (targetTime > m_timeline)
{
m_timelineTransitionEndPoint = m_timeline - (24f - targetTime + m_timeline);
}
else { m_timelineTransitionEndPoint = targetTime; }
}
m_isPlayingTimelineTransition = true;
}
/// Used to Update the time length curve when there is a change in the dawn and dusk time.
public void UpdateTimeLengthCurve()
{
m_timeLengthCurve = AnimationCurve.Linear(0f, 0f, 24f, 24f);
m_timeLengthCurve.AddKey(m_dawnTime, 6.0f);
m_timeLengthCurve.AddKey(m_duskTime, 18.0f);
}
/// Updates the time length curve based on a custom dawn and dusk time.
public void UpdateTimeLengthCurve(float dawn, float dusk)
{
m_timeLengthCurve = AnimationCurve.Linear(0f, 0f, 24f, 24f);
m_timeLengthCurve.AddKey(dawn, 6.0f);
m_timeLengthCurve.AddKey(dusk, 18.0f);
}
/// Computes the evaluation time values used by the Weather System to evaluate its curves and gradients.
private void ComputeEvaluationTime()
{
if (m_timeMode == AzureTimeMode.Simple)
{
m_evaluationTime = m_timeOfDay;
}
else
{
m_sunElevationTime = Mathf.InverseLerp(-1.0f, 1.0f, m_sunElevation);
m_evaluationTime = Mathf.Lerp(0.0f, 12.0f, m_sunElevationTime);
}
}
/// Performs the timeline transition feature.
private void ApplyTimelineTransition()
{
m_timelineTransitionStep = (Time.time - m_timelineTransitionStartTime) / m_timelineTransitionDuration;
m_temporaryTimeline = Mathf.SmoothStep(m_timelineTransitionStartPoint, m_timelineTransitionEndPoint, m_timelineTransitionStep);
m_timeline = m_temporaryTimeline;
if (m_timeDirection == AzureTimeDirection.Forward)
{
// Change to the next day in the calendar
if (m_timeline > 24.0f)
{
IncreaseDay();
m_timeline = 0.0f;
m_timelineTransitionStartPoint -= 24f;
m_timelineTransitionEndPoint -= 24f;
}
if (m_timeline >= m_timelineTransitionEndPoint)
{
m_isPlayingTimelineTransition = false;
}
}
else
{
// Change to the previous day in the calendar
if (m_timeline < 0.0f)
{
DecreaseDay();
m_timeline = 24.0f;
m_timelineTransitionStartPoint += 24f;
m_timelineTransitionEndPoint += 24f;
}
if (m_timeline <= m_timelineTransitionEndPoint)
{
m_isPlayingTimelineTransition = false;
}
}
}
/// Internal method called every time the timeline property changes.
private void OnTimelineChanged()
{
// Get the correct time of day from the timeline cycle
m_timeOfDay = m_timeLengthCurve.Evaluate(m_timeline);
// Convert the time of day to hour and minute
m_hour = (int)Mathf.Floor(m_timeOfDay);
m_minute = (int)Mathf.Floor(m_timeOfDay * 60 % 60);
ComputeEvaluationTime();
// On minute change event
if (m_previousMinute != m_minute)
{
m_previousMinute = m_minute;
AzureNotificationCenter.Invoke.OnMinuteChangedCallback(this);
}
// On hour change event
if (m_previousHour != m_hour)
{
m_previousHour = m_hour;
AzureNotificationCenter.Invoke.OnHourChangedCallback(this);
}
AzureNotificationCenter.Invoke.OnTimelineChangedCallback(this);
}
}
}