]> git.localhorst.tv Git - blobs.git/blob - src/world/world.cpp
(slightly) better camera handling
[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                 // srf  0  1  2  3  4  5
258                 //  up +Z +X +Y -Z -X -Y
259
260                 for (int index = 0, surface = 0; surface < 6; ++surface) {
261                         for (int y = 0; y < sidelength; ++y) {
262                                 for (int x = 0; x < sidelength; ++x, ++index) {
263                                         float tex = TileAt(surface, x, y).type;
264                                         attrib[4 * index + 0].position[(surface + 0) % 3] = x + 0 - offset;
265                                         attrib[4 * index + 0].position[(surface + 1) % 3] = y + 0 - offset;
266                                         attrib[4 * index + 0].position[(surface + 2) % 3] = surface < 3 ? offset : -offset;
267                                         attrib[4 * index + 0].tex_coord[0] = 0.0f;
268                                         attrib[4 * index + 0].tex_coord[1] = 0.0f;
269                                         attrib[4 * index + 0].tex_coord[2] = tex;
270
271                                         attrib[4 * index + 1].position[(surface + 0) % 3] = x + 0 - offset;
272                                         attrib[4 * index + 1].position[(surface + 1) % 3] = y + 1 - offset;
273                                         attrib[4 * index + 1].position[(surface + 2) % 3] = surface < 3 ? offset : -offset;
274                                         attrib[4 * index + 1].tex_coord[0] = 0.0f;
275                                         attrib[4 * index + 1].tex_coord[1] = 1.0f;
276                                         attrib[4 * index + 1].tex_coord[2] = tex;
277
278                                         attrib[4 * index + 2].position[(surface + 0) % 3] = x + 1 - offset;
279                                         attrib[4 * index + 2].position[(surface + 1) % 3] = y + 0 - offset;
280                                         attrib[4 * index + 2].position[(surface + 2) % 3] = surface < 3 ? offset : -offset;
281                                         attrib[4 * index + 2].tex_coord[0] = 1.0f;
282                                         attrib[4 * index + 2].tex_coord[1] = 0.0f;
283                                         attrib[4 * index + 2].tex_coord[2] = tex;
284
285                                         attrib[4 * index + 3].position[(surface + 0) % 3] = x + 1 - offset;
286                                         attrib[4 * index + 3].position[(surface + 1) % 3] = y + 1 - offset;
287                                         attrib[4 * index + 3].position[(surface + 2) % 3] = surface < 3 ? offset : -offset;
288                                         attrib[4 * index + 3].tex_coord[0] = 1.0f;
289                                         attrib[4 * index + 3].tex_coord[1] = 1.0f;
290                                         attrib[4 * index + 3].tex_coord[2] = tex;
291                                 }
292                         }
293                 }
294         }
295         vao.BindElements();
296         vao.ReserveElements(TilesTotal() * 6, GL_STATIC_DRAW);
297         {
298                 auto element = vao.MapElements(GL_WRITE_ONLY);
299                 int index = 0;
300                 for (int surface = 0; surface < 3; ++surface) {
301                         for (int y = 0; y < sidelength; ++y) {
302                                 for (int x = 0; x < sidelength; ++x, ++index) {
303                                         element[6 * index + 0] = 4 * index + 0;
304                                         element[6 * index + 1] = 4 * index + 2;
305                                         element[6 * index + 2] = 4 * index + 1;
306                                         element[6 * index + 3] = 4 * index + 1;
307                                         element[6 * index + 4] = 4 * index + 2;
308                                         element[6 * index + 5] = 4 * index + 3;
309                                 }
310                         }
311                 }
312                 for (int surface = 3; surface < 6; ++surface) {
313                         for (int y = 0; y < sidelength; ++y) {
314                                 for (int x = 0; x < sidelength; ++x, ++index) {
315                                         element[6 * index + 0] = 4 * index + 0;
316                                         element[6 * index + 1] = 4 * index + 1;
317                                         element[6 * index + 2] = 4 * index + 2;
318                                         element[6 * index + 3] = 4 * index + 2;
319                                         element[6 * index + 4] = 4 * index + 1;
320                                         element[6 * index + 5] = 4 * index + 3;
321                                 }
322                         }
323                 }
324         }
325         vao.Unbind();
326 }
327
328 void Planet::Draw(app::Assets &assets, graphics::Viewport &viewport) {
329         vao.Bind();
330         const glm::mat4 &MV = assets.shaders.planet_surface.MV();
331         assets.shaders.planet_surface.SetNormal(glm::vec3(MV * glm::vec4(0.0f, 0.0f, 1.0f, 0.0f)));
332         vao.DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 0);
333         assets.shaders.planet_surface.SetNormal(glm::vec3(MV * glm::vec4(1.0f, 0.0f, 0.0f, 0.0f)));
334         vao.DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 1);
335         assets.shaders.planet_surface.SetNormal(glm::vec3(MV * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f)));
336         vao.DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 2);
337         assets.shaders.planet_surface.SetNormal(glm::vec3(MV * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f)));
338         vao.DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 3);
339         assets.shaders.planet_surface.SetNormal(glm::vec3(MV * glm::vec4(-1.0f, 0.0f, 0.0f, 0.0f)));
340         vao.DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 4);
341         assets.shaders.planet_surface.SetNormal(glm::vec3(MV * glm::vec4(0.0f, -1.0f, 0.0f, 0.0f)));
342         vao.DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 5);
343 }
344
345
346 void GenerateTest(Planet &p) {
347         for (int surface = 0; surface <= 5; ++surface) {
348                 for (int y = 0; y < p.SideLength(); ++y) {
349                         for (int x = 0; x < p.SideLength(); ++x) {
350                                 p.TileAt(surface, x, y).type = (x == p.SideLength()/2) + (y == p.SideLength()/2);
351                         }
352                 }
353         }
354         p.BuildVAOs();
355 }
356
357
358 Sun::Sun()
359 : Body() {
360 }
361
362 Sun::~Sun() {
363 }
364
365 }
366 }