Physics Simulation & Visualization Tool 0.1
A C++ physics simulation engine with real-time 3D visualization
Loading...
Searching...
No Matches
AstronomyPresets.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <array>
5#include <cmath>
6#include <numbers>
7#include <string_view>
8
12#include "physics/Constants.h"
13
15
17 constexpr double metersPerKm = 1000.0;
18 constexpr double metersPerAu = 149597870700.0;
19 constexpr double defaultEpochJd = 2461161.5;
20 constexpr double j2000Jd = 2451545.0;
21 constexpr double julianCenturyDays = 36525.0;
22 constexpr double pi = std::numbers::pi_v<double>;
23 constexpr double degToRad = pi / 180.0;
24
25 struct OrbitalElements {
26 double semiMajorAxisAu;
27 double semiMajorAxisAuPerCentury;
28 double eccentricity;
29 double eccentricityPerCentury;
30 double inclinationDeg;
31 double inclinationDegPerCentury;
32 double meanLongitudeDeg;
33 double meanLongitudeDegPerCentury;
34 double longitudePerihelionDeg;
35 double longitudePerihelionDegPerCentury;
36 double longitudeAscendingNodeDeg;
37 double longitudeAscendingNodeDegPerCentury;
38 };
39
40 struct OrbitalState {
41 glm::vec3 position;
42 glm::vec3 velocity;
43 };
44
45 struct ThermalSpec {
46 double tempK;
47 float density;
48 float bondAlbedo;
49 float specificHeat;
50 float thermalMassFraction;
51 float conductivity;
52 float linearExpansionCoeff;
53 float meltingPoint;
54 float latentHeatFusion;
55 float boilingPoint;
56 float latentHeatVaporization;
57 };
58
59 struct BodySpec {
60 const char* name;
61 double massKg;
62 double radiusKm;
63 ThermalSpec thermal;
64 OrbitalElements orbit;
65 };
66
67 constexpr double sunMassKg = 1.9885e30;
68 constexpr double sunRadiusKm = 695700.0;
69 constexpr double sunTempK = 5772.0;
70
71 auto makeThermal = [](const ThermalSpec& spec, double internalHeatPower = 0.0) {
72 ThermalProperties thermal;
73 thermal.tempK = spec.tempK;
74 thermal.internalHeatPower = internalHeatPower;
75 thermal.referenceTempK = static_cast<float>(spec.tempK);
76 thermal.emissivity = 1.0f;
77 thermal.absorptivity = std::clamp(1.0f - spec.bondAlbedo, 0.0f, 1.0f);
78 thermal.heatTransferCoeff = 0.0f;
79 thermal.specificHeat = spec.specificHeat;
80 thermal.thermalMassFraction = spec.thermalMassFraction;
81 thermal.conductivity = spec.conductivity;
82 thermal.density = spec.density;
83 thermal.linearExpansionCoeff = spec.linearExpansionCoeff;
84 thermal.meltingPoint = spec.meltingPoint;
85 thermal.latentHeatFusion = spec.latentHeatFusion;
86 thermal.boilingPoint = spec.boilingPoint;
87 thermal.latentHeatVaporization = spec.latentHeatVaporization;
88 return thermal;
89 };
90
91 auto emittedRadiationPower = [](double radiusKm, double tempK, double emissivity) {
92 const double radiusM = radiusKm * metersPerKm;
93 const double surfaceArea = 4.0 * pi * radiusM * radiusM;
94 return emissivity * Constants::STEFAN_BOLTZMANN * surfaceArea * std::pow(tempK, 4.0);
95 };
96
97 const double sunLuminosity = emittedRadiationPower(sunRadiusKm, sunTempK, 1.0);
98
99 auto absorbedSolarPower = [&](double radiusKm, double orbitDistanceAu, double absorptivity) {
100 const double radiusM = radiusKm * metersPerKm;
101 const double projectedArea = pi * radiusM * radiusM;
102 const double orbitDistanceM = orbitDistanceAu * metersPerAu;
103 const double irradiance = sunLuminosity / (4.0 * pi * orbitDistanceM * orbitDistanceM);
104 return absorptivity * irradiance * projectedArea;
105 };
106
107 auto equilibriumInternalHeatPower = [&](double radiusKm, double orbitDistanceAu, const ThermalSpec& spec) {
108 const double emitted = emittedRadiationPower(radiusKm, spec.tempK, 1.0);
109 const double absorbed = absorbedSolarPower(radiusKm, orbitDistanceAu, 1.0 - static_cast<double>(spec.bondAlbedo));
110 return std::max(0.0, emitted - absorbed);
111 };
112
113 auto orbitalPeriodSeconds = [&](const OrbitalElements& orbit, double centralMassKg) {
114 const double semiMajorAxisM = orbit.semiMajorAxisAu * metersPerAu;
115 return 2.0 * pi * std::sqrt((semiMajorAxisM * semiMajorAxisM * semiMajorAxisM) / (Constants::G * centralMassKg));
116 };
117
118 auto thermalSkinDepthM = [&](const ThermalSpec& spec, double forcingPeriodSeconds) {
119 const double density = std::max(static_cast<double>(spec.density), 0.0);
120 const double specificHeat = std::max(static_cast<double>(spec.specificHeat), 0.0);
121 const double conductivity = std::max(static_cast<double>(spec.conductivity), 0.0);
122 if (density <= 0.0 || specificHeat <= 0.0 || conductivity <= 0.0 || forcingPeriodSeconds <= 0.0) return 0.0;
123 return std::sqrt((conductivity / (density * specificHeat)) * forcingPeriodSeconds / pi);
124 };
125
126 auto activeShellFraction = [&](double massKg, double radiusKm, const ThermalSpec& spec, double forcingPeriodSeconds) {
127 const double radiusM = radiusKm * metersPerKm;
128 const double depthM = std::min(thermalSkinDepthM(spec, forcingPeriodSeconds), radiusM);
129 if (massKg <= 0.0 || radiusM <= 0.0 || depthM <= 0.0) return 0.0f;
130
131 const double innerRadiusM = std::max(0.0, radiusM - depthM);
132 const double shellVolume = (4.0 / 3.0) * pi * (radiusM * radiusM * radiusM - innerRadiusM * innerRadiusM * innerRadiusM);
133 const double shellMass = shellVolume * static_cast<double>(spec.density);
134 return static_cast<float>(std::clamp(shellMass / massKg, 1.0e-12, 1.0));
135 };
136
137 auto makeSolarThermal = [&](double massKg, double radiusKm, double orbitDistanceAu, double forcingPeriodSeconds, const ThermalSpec& spec) {
138 ThermalSpec activeSpec = spec;
139 activeSpec.thermalMassFraction = activeShellFraction(massKg, radiusKm, spec, forcingPeriodSeconds);
140 return makeThermal(activeSpec, equilibriumInternalHeatPower(radiusKm, orbitDistanceAu, activeSpec));
141 };
142
143 auto makePlanetThermal = [&](double massKg, double radiusKm, const OrbitalElements& orbit, const ThermalSpec& spec) {
144 return makeSolarThermal(massKg, radiusKm, orbit.semiMajorAxisAu, orbitalPeriodSeconds(orbit, sunMassKg), spec);
145 };
146
147 auto makeRockySpec = [](double tempK, float density, float bondAlbedo, float specificHeat, float conductivity) {
148 return ThermalSpec{tempK, density, bondAlbedo, specificHeat, 0.0f, conductivity, 3.0e-5f, 1700.0f, 4.0e5f, 3000.0f, 1.0e7f};
149 };
150
151 auto makeFluidSpec = [](double tempK, float density, float bondAlbedo, float specificHeat, float conductivity) {
152 return ThermalSpec{tempK, density, bondAlbedo, specificHeat, 0.0f, conductivity, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
153 };
154
155 auto normalizeRadians = [](double angle) {
156 constexpr double pi = std::numbers::pi_v<double>;
157 constexpr double twoPi = 2.0 * pi;
158 angle = std::fmod(angle, twoPi);
159 if (angle < -pi) angle += twoPi;
160 if (angle > pi) angle -= twoPi;
161 return angle;
162 };
163
164 auto solveEccentricAnomaly = [&](double meanAnomalyRad, double eccentricity) {
165 double e = meanAnomalyRad + eccentricity * std::sin(meanAnomalyRad);
166 for (int i = 0; i < 12; ++i) {
167 const double delta = (meanAnomalyRad - (e - eccentricity * std::sin(e))) / (1.0 - eccentricity * std::cos(e));
168 e += delta;
169 if (std::abs(delta) <= 1.0e-12) break;
170 }
171 return e;
172 };
173
174 auto rotateOrbitToEcliptic = [](const glm::dvec3& v, double omega, double node, double inclination) {
175 const double cosOmega = std::cos(omega);
176 const double sinOmega = std::sin(omega);
177 const double cosNode = std::cos(node);
178 const double sinNode = std::sin(node);
179 const double cosI = std::cos(inclination);
180 const double sinI = std::sin(inclination);
181
182 return glm::dvec3(
183 (cosOmega * cosNode - sinOmega * sinNode * cosI) * v.x + (-sinOmega * cosNode - cosOmega * sinNode * cosI) * v.y,
184 (cosOmega * sinNode + sinOmega * cosNode * cosI) * v.x + (-sinOmega * sinNode + cosOmega * cosNode * cosI) * v.y,
185 (sinOmega * sinI) * v.x + (cosOmega * sinI) * v.y
186 );
187 };
188
189 auto eclipticToEngine = [](const glm::dvec3& v) {
190 return glm::vec3(static_cast<float>(v.x), static_cast<float>(v.z), static_cast<float>(v.y));
191 };
192
193 auto stateFromElements = [&](const OrbitalElements& elements, double centralMassKg, double epochJd) {
194 const double centuries = (epochJd - j2000Jd) / julianCenturyDays;
195 const double a = (elements.semiMajorAxisAu + elements.semiMajorAxisAuPerCentury * centuries) * metersPerAu;
196 const double e = elements.eccentricity + elements.eccentricityPerCentury * centuries;
197 const double inclination = (elements.inclinationDeg + elements.inclinationDegPerCentury * centuries) * degToRad;
198 const double meanLongitude = (elements.meanLongitudeDeg + elements.meanLongitudeDegPerCentury * centuries) * degToRad;
199 const double longitudePerihelion = (elements.longitudePerihelionDeg + elements.longitudePerihelionDegPerCentury * centuries) * degToRad;
200 const double node = (elements.longitudeAscendingNodeDeg + elements.longitudeAscendingNodeDegPerCentury * centuries) * degToRad;
201 const double omega = longitudePerihelion - node;
202 const double meanAnomaly = normalizeRadians(meanLongitude - longitudePerihelion);
203 const double eccentricAnomaly = solveEccentricAnomaly(meanAnomaly, e);
204 const double cosE = std::cos(eccentricAnomaly);
205 const double sinE = std::sin(eccentricAnomaly);
206 const double oneMinusECosE = 1.0 - e * cosE;
207 const double sqrtOneMinusESq = std::sqrt(1.0 - e * e);
208 const double meanMotion = std::sqrt(Constants::G * centralMassKg / (a * a * a));
209 const double eccentricAnomalyRate = meanMotion / oneMinusECosE;
210
211 const glm::dvec3 orbitalPosition(a * (cosE - e), a * sqrtOneMinusESq * sinE, 0.0);
212 const glm::dvec3 orbitalVelocity(-a * sinE * eccentricAnomalyRate, a * sqrtOneMinusESq * cosE * eccentricAnomalyRate, 0.0);
213
214 return OrbitalState{
215 eclipticToEngine(rotateOrbitToEcliptic(orbitalPosition, omega, node, inclination)),
216 eclipticToEngine(rotateOrbitToEcliptic(orbitalVelocity, omega, node, inclination))
217 };
218 };
219
220 auto createSolarBody = [&](const BodySpec& spec) {
221 const OrbitalState state = stateFromElements(spec.orbit, sunMassKg, defaultEpochJd);
222 PointMassOptions options;
223 options.base.position = state.position;
224 options.base.scale = glm::vec3(static_cast<float>(spec.radiusKm * 2.0 * metersPerKm));
225 options.mass = spec.massKg;
226 options.velocity = state.velocity;
227
228 SceneObject* body = sceneManager.createObject("prim_sphere", ResourceManager::getShader("basic"), options);
229 sceneManager.setObjectName(body, spec.name);
230 body->getPhysicsBody()->setThermalProperty(makePlanetThermal(spec.massKg, spec.radiusKm, spec.orbit, spec.thermal), BodyLock::NOLOCK);
231 return body;
232 };
233
234 sceneManager.setGlobalAcceleration(glm::vec3(0.0f));
235 sceneManager.physicsSystem->setAmbientTemperature(2.725f);
236 sceneManager.physicsSystem->setGravitationalConstant(Constants::G);
237 sceneManager.setSimSpeed(1.0e6f);
238
239 PointMassOptions sunOptions;
240 sunOptions.base.position = glm::vec3(0.0f);
241 sunOptions.base.scale = glm::vec3(static_cast<float>(sunRadiusKm * 2.0 * metersPerKm));
242 sunOptions.mass = sunMassKg;
243 SceneObject* sun = sceneManager.createObject("prim_sphere", ResourceManager::getShader("basic"), sunOptions);
244 sceneManager.setObjectName(sun, "Sun");
245 sun->getPhysicsBody()->setThermalProperty(makeThermal(makeFluidSpec(sunTempK, 1408.0f, 0.0f, 20780.0f, 1.0e4f), sunLuminosity), BodyLock::NOLOCK);
246
247 const std::array<BodySpec, 8> planets{{
248 {"Mercury", 0.33010e24, 2439.7, makeRockySpec(440.15, 5429.0f, 0.068f, 800.0f, 2.0f), {0.38709927, 0.00000037, 0.20563593, 0.00001906, 7.00497902, -0.00594749, 252.25032350, 149472.67411175, 77.45779628, 0.16047689, 48.33076593, -0.12534081}},
249 {"Venus", 4.8673e24, 6051.8, makeRockySpec(737.15, 5243.0f, 0.770f, 850.0f, 2.0f), {0.72333566, 0.00000390, 0.00677672, -0.00004107, 3.39467605, -0.00078890, 181.97909950, 58517.81538729, 131.60246718, 0.00268329, 76.67984255, -0.27769418}},
250 {"Earth", 5.9722e24, 6371.0, makeRockySpec(288.15, 5513.0f, 0.294f, 1000.0f, 2.5f), {1.00000261, 0.00000562, 0.01671123, -0.00004392, -0.00001531, -0.01294668, 100.46457166, 35999.37244981, 102.93768193, 0.32327364, 0.0, 0.0}},
251 {"Mars", 0.64169e24, 3389.5, makeRockySpec(208.15, 3934.0f, 0.250f, 750.0f, 0.08f), {1.52371034, 0.00001847, 0.09339410, 0.00007882, 1.84969142, -0.00813131, -4.55343205, 19140.30268499, -23.94362959, 0.44441088, 49.55953891, -0.29257343}},
252 {"Jupiter", 1898.13e24, 69911.0, makeFluidSpec(163.15, 1326.0f, 0.343f, 13000.0f, 0.18f), {5.20288700, -0.00011607, 0.04838624, -0.00013253, 1.30439695, -0.00183714, 34.39644051, 3034.74612775, 14.72847983, 0.21252668, 100.47390909, 0.20469106}},
253 {"Saturn", 568.32e24, 58232.0, makeFluidSpec(133.15, 687.0f, 0.342f, 13000.0f, 0.18f), {9.53667594, -0.00125060, 0.05386179, -0.00050991, 2.48599187, 0.00193609, 49.95424423, 1222.49362201, 92.59887831, -0.41897216, 113.66242448, -0.28867794}},
254 {"Uranus", 86.811e24, 25362.0, makeFluidSpec(78.15, 1270.0f, 0.300f, 9000.0f, 0.6f), {19.18916464, -0.00196176, 0.04725744, -0.00004397, 0.77263783, -0.00242939, 313.23810451, 428.48202785, 170.95427630, 0.40805281, 74.01692503, 0.04240589}},
255 {"Neptune", 102.409e24, 24622.0, makeFluidSpec(73.15, 1638.0f, 0.290f, 9000.0f, 0.6f), {30.06992276, 0.00026291, 0.00859048, 0.00005105, 1.77004347, 0.00035372, -55.12002969, 218.45945325, 44.96476227, -0.32241464, 131.78422574, -0.00508664}},
256 }};
257
258 SceneObject* earth = nullptr;
259 OrbitalState earthState{};
260 for (const BodySpec& planet : planets) {
261 SceneObject* body = createSolarBody(planet);
262 if (std::string_view(planet.name) == "Earth") {
263 earth = body;
264 earthState.position = body->getPosition();
265 earthState.velocity = body->getPhysicsBody()->getVelocity(BodyLock::NOLOCK);
266 }
267 }
268
269 const OrbitalState moonRelativeState = stateFromElements(
270 {0.002569555, 0.0, 0.0549, 0.0, 5.1454, 0.0, 558.5516, 481267.88088047254, 443.1862, 4069.01334885, 125.1228, -1934.1378481575},
271 5.9724e24,
272 defaultEpochJd
273 );
274 PointMassOptions moonOptions;
275 moonOptions.base.position = earthState.position + moonRelativeState.position;
276 moonOptions.base.scale = glm::vec3(1737.4f * 2.0f * static_cast<float>(metersPerKm));
277 moonOptions.mass = 0.07346e24;
278 moonOptions.velocity = earthState.velocity + moonRelativeState.velocity;
279 SceneObject* moon = sceneManager.createObject("prim_sphere", ResourceManager::getShader("basic"), moonOptions);
280 sceneManager.setObjectName(moon, "Moon");
281 const double earthYearSeconds = orbitalPeriodSeconds({1.00000261, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, sunMassKg);
282 moon->getPhysicsBody()->setThermalProperty(makeSolarThermal(0.07346e24, 1737.4, 1.00000261, earthYearSeconds, makeRockySpec(270.4, 3344.0f, 0.110f, 741.0f, 0.001f)), BodyLock::NOLOCK);
283
284 sceneManager.focusObject(earth);
285}
286
287}
glm::vec3 getVelocity(BodyLock lock) const
virtual void setThermalProperty(const ThermalProperties &newProps, BodyLock lock)
static Shader * getShader(const std::string &name)
SceneObject * createObject(const std::string &meshName, Shader *shader=ResourceManager::getShader("basic"), const CreationOptions &=ObjectOptions{})
void focusObject(SceneObject *target)
void setSimSpeed(float newSpeed)
void setGlobalAcceleration(const glm::vec3 &newAcceleration) const
std::unique_ptr< Physics::PhysicsSystem > physicsSystem
void setObjectName(SceneObject *obj, const std::string &newName)
glm::vec3 getPosition() const
Physics::PhysicsBody * getPhysicsBody() const
Definition SceneObject.h:39
constexpr double G
Definition Constants.h:5
constexpr double STEFAN_BOLTZMANN
Definition Constants.h:6
void createRealSolarSystem(SceneManager &sceneManager)