]> git.localhorst.tv Git - blobs.git/blob - src/world/world.cpp
more orbits and stuff
[blobs.git] / src / world / world.cpp
1 #include "Body.hpp"
2 #include "Orbit.hpp"
3 #include "Planet.hpp"
4 #include "Simulation.hpp"
5 #include "Sun.hpp"
6 #include "Tile.hpp"
7
8 #include "../const.hpp"
9 #include "../app/Assets.hpp"
10 #include "../graphics/Viewport.hpp"
11
12 #include <algorithm>
13 #include <cmath>
14 #include <glm/gtc/matrix_transform.hpp>
15 #include <glm/gtx/euler_angles.hpp>
16 #include <glm/gtx/transform.hpp>
17
18 using blobs::G;
19 using blobs::PI_2p0;
20
21 using std::sin;
22 using std::cos;
23 using std::pow;
24 using std::sqrt;
25
26
27 namespace blobs {
28 namespace world {
29
30 Body::Body()
31 : sim(nullptr)
32 , parent(nullptr)
33 , children()
34 , mass(1.0)
35 , radius(1.0)
36 , orbit()
37 , surface_tilt(0.0, 0.0)
38 , axis_tilt(0.0, 0.0)
39 , rotation(0.0)
40 , angular(0.0) {
41 }
42
43 Body::~Body() {
44 }
45
46 void Body::SetSimulation(Simulation &s) noexcept {
47         sim = &s;
48         for (auto child : children) {
49                 child->SetSimulation(s);
50         }
51 }
52
53 void Body::SetParent(Body &p) {
54         if (HasParent()) {
55                 UnsetParent();
56         }
57         parent = &p;
58         parent->AddChild(*this);
59 }
60
61 void Body::UnsetParent() {
62         if (!HasParent()) return;
63         parent->RemoveChild(*this);
64         parent = nullptr;
65 }
66
67 void Body::AddChild(Body &c) {
68         children.push_back(&c);
69         c.SetSimulation(*sim);
70 }
71
72 void Body::RemoveChild(Body &c) {
73         auto entry = std::find(children.begin(), children.end(), &c);
74         if (entry != children.end()) {
75                 children.erase(entry);
76         }
77 }
78
79 double Body::Inertia() const noexcept {
80         // assume solid sphere for now
81         return (2.0/5.0) * Mass() * pow(Radius(), 2);
82 }
83
84 double Body::GravitationalParameter() const noexcept {
85         return G * Mass();
86 }
87
88 double Body::OrbitalPeriod() const noexcept {
89         if (parent) {
90                 return PI_2p0 * sqrt(pow(orbit.SemiMajorAxis(), 3) / (G * (parent->Mass() + Mass())));
91         } else {
92                 return 0.0;
93         }
94 }
95
96 double Body::RotationalPeriod() const noexcept {
97         if (std::abs(angular) < std::numeric_limits<double>::epsilon()) {
98                 return std::numeric_limits<double>::infinity();
99         } else {
100                 return PI_2p0 * Inertia() / angular;
101         }
102 }
103
104 glm::dmat4 Body::LocalTransform() const noexcept {
105         glm::dmat4 srf = glm::eulerAngleXY(surface_tilt.x, surface_tilt.y);
106         glm::dmat4 rot = glm::eulerAngleY(rotation);
107         glm::dmat4 tilt = glm::eulerAngleXY(axis_tilt.x, axis_tilt.y);
108         return tilt * rot * srf;
109 }
110
111 glm::dmat4 Body::InverseTransform() const noexcept {
112         glm::dmat4 srf = glm::eulerAngleYX(-surface_tilt.y, -surface_tilt.x);
113         glm::dmat4 rot = glm::eulerAngleY(-rotation);
114         glm::dmat4 tilt = glm::eulerAngleYX(-axis_tilt.y, -axis_tilt.x);
115         return srf * rot * tilt;
116 }
117
118 glm::dmat4 Body::ToParent() const noexcept {
119         if (!parent) {
120                 return glm::dmat4(1.0);
121         }
122         return orbit.InverseMatrix(PI_2p0 * (GetSimulation().Time() / OrbitalPeriod()));
123 }
124
125 glm::dmat4 Body::FromParent() const noexcept {
126         if (!parent) {
127                 return glm::dmat4(1.0);
128         }
129         return orbit.Matrix(PI_2p0 * (GetSimulation().Time() / OrbitalPeriod()));
130 }
131
132
133 Orbit::Orbit()
134 : sma(1.0)
135 , ecc(0.0)
136 , inc(0.0)
137 , asc(0.0)
138 , arg(0.0)
139 , mna(0.0) {
140 }
141
142 Orbit::~Orbit() {
143 }
144
145 double Orbit::SemiMajorAxis() const noexcept {
146         return sma;
147 }
148
149 Orbit &Orbit::SemiMajorAxis(double s) noexcept {
150         sma = s;
151         return *this;
152 }
153
154 double Orbit::Eccentricity() const noexcept {
155         return ecc;
156 }
157
158 Orbit &Orbit::Eccentricity(double e) noexcept {
159         ecc = e;
160         return *this;
161 }
162
163 double Orbit::Inclination() const noexcept {
164         return inc;
165 }
166
167 Orbit &Orbit::Inclination(double i) noexcept {
168         inc = i;
169         return *this;
170 }
171
172 double Orbit::LongitudeAscending() const noexcept {
173         return asc;
174 }
175
176 Orbit &Orbit::LongitudeAscending(double l) noexcept {
177         asc = l;
178         return *this;
179 }
180
181 double Orbit::ArgumentPeriapsis() const noexcept {
182         return arg;
183 }
184
185 Orbit &Orbit::ArgumentPeriapsis(double a) noexcept {
186         arg = a;
187         return *this;
188 }
189
190 double Orbit::MeanAnomaly() const noexcept {
191         return mna;
192 }
193
194 Orbit &Orbit::MeanAnomaly(double m) noexcept {
195         mna = m;
196         return *this;
197 }
198
199 namespace {
200
201 double mean2eccentric(double M, double e) {
202         double E = M; // eccentric anomaly, solve M = E - e sin E
203         // limit to 100 steps to prevent deadlocks in impossible situations
204         for (int i = 0; i < 100; ++i) {
205                 double dE = (E - e * sin(E) - M) / (1 - e * cos(E));
206                 E -= dE;
207                 if (abs(dE) < 1.0e-6) break;
208         }
209         return E;
210 }
211
212 }
213
214 glm::dmat4 Orbit::Matrix(double t) const noexcept {
215         double M = mna + t;
216         double E = mean2eccentric(M, ecc);
217
218         // coordinates in orbital plane, P=x, Q=-z
219         double P = sma * (cos(E) - ecc);
220         double Q = sma * sin(E) * sqrt(1 - (ecc * ecc));
221
222         return glm::translate(glm::yawPitchRoll(asc, inc, arg), glm::dvec3(P, 0.0, -Q));
223 }
224
225 glm::dmat4 Orbit::InverseMatrix(double t) const noexcept {
226         double M = mna + t;
227         double E = mean2eccentric(M, ecc);
228         double P = sma * (cos(E) - ecc);
229         double Q = sma * sin(E) * sqrt(1 - (ecc * ecc));
230         return glm::transpose(glm::yawPitchRoll(asc, inc, arg)) * glm::translate(glm::dvec3(-P, 0.0, Q));
231 }
232
233
234 Planet::Planet(int sidelength)
235 : Body()
236 , sidelength(sidelength)
237 , tiles(new Tile[TilesTotal()])
238 , vao() {
239         Radius(double(sidelength) / 2.0);
240 }
241
242 Planet::~Planet() {
243 }
244
245 void Planet::BuildVAOs() {
246         vao.Bind();
247         vao.BindAttributes();
248         vao.EnableAttribute(0);
249         vao.EnableAttribute(1);
250         vao.AttributePointer<glm::vec3>(0, false, offsetof(Attributes, position));
251         vao.AttributePointer<glm::vec3>(1, false, offsetof(Attributes, tex_coord));
252         vao.ReserveAttributes(TilesTotal() * 4, GL_STATIC_DRAW);
253         {
254                 auto attrib = vao.MapAttributes(GL_WRITE_ONLY);
255                 float offset = sidelength * 0.5f;
256
257                 for (int index = 0, surface = 0; surface < 6; ++surface) {
258                         for (int y = 0; y < sidelength; ++y) {
259                                 for (int x = 0; x < sidelength; ++x, ++index) {
260                                         float tex = TileAt(surface, x, y).type;
261                                         attrib[4 * index + 0].position[(surface + 0) % 3] = x + 0 - offset;
262                                         attrib[4 * index + 0].position[(surface + 1) % 3] = y + 0 - offset;
263                                         attrib[4 * index + 0].position[(surface + 2) % 3] = surface < 3 ? offset : -offset;
264                                         attrib[4 * index + 0].tex_coord[0] = 0.0f;
265                                         attrib[4 * index + 0].tex_coord[1] = 0.0f;
266                                         attrib[4 * index + 0].tex_coord[2] = tex;
267
268                                         attrib[4 * index + 1].position[(surface + 0) % 3] = x + 0 - offset;
269                                         attrib[4 * index + 1].position[(surface + 1) % 3] = y + 1 - offset;
270                                         attrib[4 * index + 1].position[(surface + 2) % 3] = surface < 3 ? offset : -offset;
271                                         attrib[4 * index + 1].tex_coord[0] = 0.0f;
272                                         attrib[4 * index + 1].tex_coord[1] = 1.0f;
273                                         attrib[4 * index + 1].tex_coord[2] = tex;
274
275                                         attrib[4 * index + 2].position[(surface + 0) % 3] = x + 1 - offset;
276                                         attrib[4 * index + 2].position[(surface + 1) % 3] = y + 0 - offset;
277                                         attrib[4 * index + 2].position[(surface + 2) % 3] = surface < 3 ? offset : -offset;
278                                         attrib[4 * index + 2].tex_coord[0] = 1.0f;
279                                         attrib[4 * index + 2].tex_coord[1] = 0.0f;
280                                         attrib[4 * index + 2].tex_coord[2] = tex;
281
282                                         attrib[4 * index + 3].position[(surface + 0) % 3] = x + 1 - offset;
283                                         attrib[4 * index + 3].position[(surface + 1) % 3] = y + 1 - offset;
284                                         attrib[4 * index + 3].position[(surface + 2) % 3] = surface < 3 ? offset : -offset;
285                                         attrib[4 * index + 3].tex_coord[0] = 1.0f;
286                                         attrib[4 * index + 3].tex_coord[1] = 1.0f;
287                                         attrib[4 * index + 3].tex_coord[2] = tex;
288                                 }
289                         }
290                 }
291         }
292         vao.BindElements();
293         vao.ReserveElements(TilesTotal() * 6, GL_STATIC_DRAW);
294         {
295                 auto element = vao.MapElements(GL_WRITE_ONLY);
296                 int index = 0;
297                 for (int surface = 0; surface < 3; ++surface) {
298                         for (int y = 0; y < sidelength; ++y) {
299                                 for (int x = 0; x < sidelength; ++x, ++index) {
300                                         element[6 * index + 0] = 4 * index + 0;
301                                         element[6 * index + 1] = 4 * index + 2;
302                                         element[6 * index + 2] = 4 * index + 1;
303                                         element[6 * index + 3] = 4 * index + 1;
304                                         element[6 * index + 4] = 4 * index + 2;
305                                         element[6 * index + 5] = 4 * index + 3;
306                                 }
307                         }
308                 }
309                 for (int surface = 3; surface < 6; ++surface) {
310                         for (int y = 0; y < sidelength; ++y) {
311                                 for (int x = 0; x < sidelength; ++x, ++index) {
312                                         element[6 * index + 0] = 4 * index + 0;
313                                         element[6 * index + 1] = 4 * index + 1;
314                                         element[6 * index + 2] = 4 * index + 2;
315                                         element[6 * index + 3] = 4 * index + 2;
316                                         element[6 * index + 4] = 4 * index + 1;
317                                         element[6 * index + 5] = 4 * index + 3;
318                                 }
319                         }
320                 }
321         }
322         vao.Unbind();
323 }
324
325 void Planet::Draw(app::Assets &assets, graphics::Viewport &viewport) {
326         vao.Bind();
327         const glm::mat4 &MV = assets.shaders.planet_surface.MV();
328         assets.shaders.planet_surface.SetNormal(glm::vec3(MV * glm::vec4(0.0f, 0.0f, 1.0f, 0.0f)));
329         vao.DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 0);
330         assets.shaders.planet_surface.SetNormal(glm::vec3(MV * glm::vec4(1.0f, 0.0f, 0.0f, 0.0f)));
331         vao.DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 1);
332         assets.shaders.planet_surface.SetNormal(glm::vec3(MV * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f)));
333         vao.DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 2);
334         assets.shaders.planet_surface.SetNormal(glm::vec3(MV * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f)));
335         vao.DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 3);
336         assets.shaders.planet_surface.SetNormal(glm::vec3(MV * glm::vec4(-1.0f, 0.0f, 0.0f, 0.0f)));
337         vao.DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 4);
338         assets.shaders.planet_surface.SetNormal(glm::vec3(MV * glm::vec4(0.0f, -1.0f, 0.0f, 0.0f)));
339         vao.DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 5);
340 }
341
342
343 void GenerateTest(Planet &p) {
344         for (int surface = 0; surface <= 5; ++surface) {
345                 for (int y = 0; y < p.SideLength(); ++y) {
346                         for (int x = 0; x < p.SideLength(); ++x) {
347                                 p.TileAt(surface, x, y).type = (x == p.SideLength()/2) + (y == p.SideLength()/2);
348                         }
349                 }
350         }
351         p.BuildVAOs();
352 }
353
354
355 Sun::Sun()
356 : Body() {
357 }
358
359 Sun::~Sun() {
360 }
361
362 }
363 }