]> git.localhorst.tv Git - blobs.git/blob - src/world/world.cpp
aggression
[blobs.git] / src / world / world.cpp
1 #include "Body.hpp"
2 #include "CreatureCreatureCollision.hpp"
3 #include "Orbit.hpp"
4 #include "Planet.hpp"
5 #include "Resource.hpp"
6 #include "Set.hpp"
7 #include "Simulation.hpp"
8 #include "Sun.hpp"
9 #include "Tile.hpp"
10 #include "TileType.hpp"
11
12 #include "../app/Assets.hpp"
13 #include "../creature/Composition.hpp"
14 #include "../creature/Creature.hpp"
15 #include "../graphics/Viewport.hpp"
16 #include "../math/const.hpp"
17 #include "../math/geometry.hpp"
18 #include "../math/OctaveNoise.hpp"
19 #include "../math/SimplexNoise.hpp"
20
21 #include <algorithm>
22 #include <cmath>
23 #include <iostream>
24 #include <glm/gtc/matrix_transform.hpp>
25 #include <glm/gtx/euler_angles.hpp>
26 #include <glm/gtx/io.hpp>
27 #include <glm/gtx/transform.hpp>
28
29 using std::sin;
30 using std::cos;
31 using std::pow;
32 using std::sqrt;
33
34
35 namespace blobs {
36 namespace world {
37
38 Body::Body()
39 : sim(nullptr)
40 , parent(nullptr)
41 , children()
42 , mass(1.0)
43 , radius(1.0)
44 , orbit()
45 , surface_tilt(0.0, 0.0)
46 , axis_tilt(0.0, 0.0)
47 , rotation(0.0)
48 , angular(0.0)
49 , orbital(1.0)
50 , inverse_orbital(1.0)
51 , local(1.0)
52 , inverse_local(1.0)
53 , creatures()
54 , atmosphere(-1) {
55 }
56
57 Body::~Body() {
58 }
59
60 void Body::SetSimulation(Simulation &s) noexcept {
61         sim = &s;
62         for (auto child : children) {
63                 child->SetSimulation(s);
64         }
65 }
66
67 void Body::SetParent(Body &p) {
68         if (HasParent()) {
69                 UnsetParent();
70         }
71         parent = &p;
72         parent->AddChild(*this);
73 }
74
75 void Body::UnsetParent() {
76         if (!HasParent()) return;
77         parent->RemoveChild(*this);
78         parent = nullptr;
79 }
80
81 void Body::AddChild(Body &c) {
82         children.push_back(&c);
83         c.SetSimulation(*sim);
84 }
85
86 void Body::RemoveChild(Body &c) {
87         auto entry = std::find(children.begin(), children.end(), &c);
88         if (entry != children.end()) {
89                 children.erase(entry);
90         }
91 }
92
93 double Body::Inertia() const noexcept {
94         // assume solid sphere for now
95         return (2.0/5.0) * Mass() * pow(Radius(), 2);
96 }
97
98 double Body::GravitationalParameter() const noexcept {
99         return G * Mass();
100 }
101
102 double Body::OrbitalPeriod() const noexcept {
103         if (parent) {
104                 return PI * 2.0 * sqrt(pow(orbit.SemiMajorAxis(), 3) / (G * (parent->Mass() + Mass())));
105         } else {
106                 return 0.0;
107         }
108 }
109
110 double Body::RotationalPeriod() const noexcept {
111         if (std::abs(angular) < std::numeric_limits<double>::epsilon()) {
112                 return std::numeric_limits<double>::infinity();
113         } else {
114                 return PI * 2.0 * Inertia() / angular;
115         }
116 }
117
118 double Body::SphereOfInfluence() const noexcept {
119         if (HasParent()) {
120                 return orbit.SemiMajorAxis() * std::pow(Mass() / Parent().Mass(), 2.0 / 5.0);
121         } else {
122                 return std::numeric_limits<double>::infinity();
123         }
124 }
125
126 glm::dmat4 Body::ToUniverse() const noexcept {
127         glm::dmat4 m(1.0);
128         const Body *b = this;
129         while (b->HasParent()) {
130                 m = b->ToParent() * m;
131                 b = &b->Parent();
132         }
133         return m;
134 }
135
136 glm::dmat4 Body::FromUniverse() const noexcept {
137         glm::dmat4 m(1.0);
138         const Body *b = this;
139         while (b->HasParent()) {
140                 m *= b->FromParent();
141                 b = &b->Parent();
142         }
143         return m;
144 }
145
146 namespace {
147 std::vector<creature::Creature *> ccache;
148 std::vector<CreatureCreatureCollision> collisions;
149 }
150
151 void Body::Tick(double dt) {
152         rotation += dt * AngularMomentum() / Inertia();
153         Cache();
154         ccache = Creatures();
155         for (creature::Creature *c : ccache) {
156                 c->Tick(dt);
157         }
158         // first remove creatures so they don't collide
159         for (auto c = Creatures().begin(); c != Creatures().end();) {
160                 if ((*c)->Removable()) {
161                         (*c)->Removed();
162                         c = Creatures().erase(c);
163                 } else {
164                         ++c;
165                 }
166         }
167         CheckCollision();
168 }
169
170 void Body::Cache() noexcept {
171         if (parent) {
172                 orbital =
173                         orbit.Matrix(PI * 2.0 * (GetSimulation().Time() / OrbitalPeriod()))
174                         * glm::eulerAngleXY(axis_tilt.x, axis_tilt.y);
175                 inverse_orbital =
176                         glm::eulerAngleYX(-axis_tilt.y, -axis_tilt.x)
177                         * orbit.InverseMatrix(PI * 2.0 * (GetSimulation().Time() / OrbitalPeriod()));
178         } else {
179                 orbital = glm::eulerAngleXY(axis_tilt.x, axis_tilt.y);
180                 inverse_orbital = glm::eulerAngleYX(-axis_tilt.y, -axis_tilt.x);
181         }
182         local =
183                 glm::eulerAngleY(rotation)
184                 * glm::eulerAngleXY(surface_tilt.x, surface_tilt.y);
185         inverse_local =
186                 glm::eulerAngleYX(-surface_tilt.y, -surface_tilt.x)
187                 * glm::eulerAngleY(-rotation);
188 }
189
190 void Body::CheckCollision() noexcept {
191         if (Creatures().size() < 2) return;
192         collisions.clear();
193         auto end = Creatures().end();
194         for (auto i = Creatures().begin(); i != end; ++i) {
195                 math::AABB i_box((*i)->CollisionBounds());
196                 glm::dmat4 i_mat((*i)->CollisionTransform());
197                 for (auto j = (i + 1); j != end; ++j) {
198                         glm::dvec3 diff((*i)->GetSituation().Position() - (*j)->GetSituation().Position());
199                         double max_dist = ((*i)->Size() + (*j)->Size()) * 1.74;
200                         if (glm::length2(diff) > max_dist * max_dist) continue;
201                         math::AABB j_box((*j)->CollisionBounds());
202                         glm::dmat4 j_mat((*j)->CollisionTransform());
203                         glm::dvec3 normal;
204                         double depth;
205                         if (Intersect(i_box, i_mat, j_box, j_mat, normal, depth)) {
206                                 collisions.push_back({ **i, **j, normal, depth });
207                         }
208                 }
209         }
210         for (auto &c : collisions) {
211                 c.A().OnCollide(c.B());
212                 c.B().OnCollide(c.A());
213                 c.A().GetSituation().Move(c.Normal() * (c.Depth() * -0.5));
214                 c.B().GetSituation().Move(c.Normal() * (c.Depth() * 0.5));
215                 c.A().GetSituation().Accelerate(c.Normal() * -glm::dot(c.Normal(), c.AVel()));
216                 c.B().GetSituation().Accelerate(c.Normal() * -glm::dot(c.Normal(), c.BVel()));
217         }
218 }
219
220 void Body::AddCreature(creature::Creature *c) {
221         creatures.push_back(c);
222 }
223
224 void Body::RemoveCreature(creature::Creature *c) {
225         auto entry = std::find(creatures.begin(), creatures.end(), c);
226         if (entry != creatures.end()) {
227                 creatures.erase(entry);
228         }
229 }
230
231
232 CreatureCreatureCollision::~CreatureCreatureCollision() {
233 }
234
235 const glm::dvec3 &CreatureCreatureCollision::APos() const noexcept {
236         return a->GetSituation().Position();
237 }
238
239 const glm::dvec3 &CreatureCreatureCollision::AVel() const noexcept {
240         return a->GetSituation().Velocity();
241 }
242
243 const glm::dvec3 &CreatureCreatureCollision::BPos() const noexcept {
244         return b->GetSituation().Position();
245 }
246
247 const glm::dvec3 &CreatureCreatureCollision::BVel() const noexcept {
248         return b->GetSituation().Velocity();
249 }
250
251
252 Orbit::Orbit()
253 : sma(1.0)
254 , ecc(0.0)
255 , inc(0.0)
256 , asc(0.0)
257 , arg(0.0)
258 , mna(0.0) {
259 }
260
261 Orbit::~Orbit() {
262 }
263
264 double Orbit::SemiMajorAxis() const noexcept {
265         return sma;
266 }
267
268 Orbit &Orbit::SemiMajorAxis(double s) noexcept {
269         sma = s;
270         return *this;
271 }
272
273 double Orbit::Eccentricity() const noexcept {
274         return ecc;
275 }
276
277 Orbit &Orbit::Eccentricity(double e) noexcept {
278         ecc = e;
279         return *this;
280 }
281
282 double Orbit::Inclination() const noexcept {
283         return inc;
284 }
285
286 Orbit &Orbit::Inclination(double i) noexcept {
287         inc = i;
288         return *this;
289 }
290
291 double Orbit::LongitudeAscending() const noexcept {
292         return asc;
293 }
294
295 Orbit &Orbit::LongitudeAscending(double l) noexcept {
296         asc = l;
297         return *this;
298 }
299
300 double Orbit::ArgumentPeriapsis() const noexcept {
301         return arg;
302 }
303
304 Orbit &Orbit::ArgumentPeriapsis(double a) noexcept {
305         arg = a;
306         return *this;
307 }
308
309 double Orbit::MeanAnomaly() const noexcept {
310         return mna;
311 }
312
313 Orbit &Orbit::MeanAnomaly(double m) noexcept {
314         mna = m;
315         return *this;
316 }
317
318 namespace {
319
320 double mean2eccentric(double M, double e) {
321         double E = M; // eccentric anomaly, solve M = E - e sin E
322         // limit to 100 steps to prevent deadlocks in impossible situations
323         for (int i = 0; i < 100; ++i) {
324                 double dE = (E - e * sin(E) - M) / (1 - e * cos(E));
325                 E -= dE;
326                 if (std::abs(dE) < 1.0e-6) break;
327         }
328         return E;
329 }
330
331 }
332
333 glm::dmat4 Orbit::Matrix(double t) const noexcept {
334         double M = mna + t;
335         double E = mean2eccentric(M, ecc);
336
337         // coordinates in orbital plane, P=x, Q=-z
338         double P = sma * (cos(E) - ecc);
339         double Q = sma * sin(E) * sqrt(1 - (ecc * ecc));
340
341         return glm::yawPitchRoll(asc, inc, arg) * glm::translate(glm::dvec3(P, 0.0, -Q));
342 }
343
344 glm::dmat4 Orbit::InverseMatrix(double t) const noexcept {
345         double M = mna + t;
346         double E = mean2eccentric(M, ecc);
347         double P = sma * (cos(E) - ecc);
348         double Q = sma * sin(E) * sqrt(1 - (ecc * ecc));
349         return glm::translate(glm::dvec3(-P, 0.0, Q)) * glm::transpose(glm::yawPitchRoll(asc, inc, arg));
350 }
351
352
353 Planet::Planet(int sidelength)
354 : Body()
355 , sidelength(sidelength)
356 , tiles(TilesTotal())
357 , vao() {
358         Radius(double(sidelength) / 2.0);
359 }
360
361 Planet::~Planet() {
362 }
363
364 namespace {
365 /// map p onto cube, s gives the surface, u and v the position in [-1,1]
366 void cubemap(const glm::dvec3 &p, int &s, double &u, double &v) noexcept {
367         const glm::dvec3 p_abs(glm::abs(p));
368         const glm::bvec3 p_pos(glm::greaterThan(p, glm::dvec3(0.0)));
369         double max_axis = 0.0;
370
371         if (p_pos.x && p_abs.x >= p_abs.y && p_abs.x >= p_abs.z) {
372                 max_axis = p_abs.x;
373                 u = p.y;
374                 v = p.z;
375                 s = 1;
376         }
377         if (!p_pos.x && p_abs.x >= p_abs.y && p_abs.x >= p_abs.z) {
378                 max_axis = p_abs.x;
379                 u = -p.y;
380                 v = -p.z;
381                 s = 4;
382         }
383         if (p_pos.y && p_abs.y >= p_abs.x && p_abs.y >= p_abs.z) {
384                 max_axis = p_abs.y;
385                 u = p.z;
386                 v = p.x;
387                 s = 2;
388         }
389         if (!p_pos.y && p_abs.y >= p_abs.x && p_abs.y >= p_abs.z) {
390                 max_axis = p_abs.y;
391                 u = -p.z;
392                 v = -p.x;
393                 s = 5;
394         }
395         if (p_pos.z && p_abs.z >= p_abs.x && p_abs.z >= p_abs.y) {
396                 max_axis = p_abs.z;
397                 u = p.x;
398                 v = p.y;
399                 s = 0;
400         }
401         if (!p_pos.z && p_abs.z >= p_abs.x && p_abs.z >= p_abs.y) {
402                 max_axis = p_abs.z;
403                 u = -p.x;
404                 v = -p.y;
405                 s = 3;
406         }
407         u /= max_axis;
408         v /= max_axis;
409 }
410 /// get p from cube, s being surface, u and v the position in [-1,1],
411 /// gives a vector from the center to the surface
412 glm::dvec3 cubeunmap(int s, double u, double v) {
413         switch (s) {
414                 default:
415                 case 0: return glm::dvec3(u, v, 1.0); // +Z
416                 case 1: return glm::dvec3(1.0, u, v); // +X
417                 case 2: return glm::dvec3(v, 1.0, u); // +Y
418                 case 3: return glm::dvec3(-u, -v, -1.0); // -Z
419                 case 4: return glm::dvec3(-1.0, -u, -v); // -X
420                 case 5: return glm::dvec3(-v, -1.0, -u); // -Y
421         };
422 }
423 }
424
425 Tile &Planet::TileAt(const glm::dvec3 &p) noexcept {
426         int srf = 0;
427         double u = 0.0;
428         double v = 0.0;
429         cubemap(p, srf, u, v);
430         int x = glm::clamp(int(u * Radius() + Radius()), 0, sidelength - 1);
431         int y = glm::clamp(int(v * Radius() + Radius()), 0, sidelength - 1);
432         return TileAt(srf, x, y);
433 }
434
435 const Tile &Planet::TileAt(const glm::dvec3 &p) const noexcept {
436         int srf = 0;
437         double u = 0.0;
438         double v = 0.0;
439         cubemap(p, srf, u, v);
440         int x = glm::clamp(int(u * Radius() + Radius()), 0, sidelength - 1);
441         int y = glm::clamp(int(v * Radius() + Radius()), 0, sidelength - 1);
442         return TileAt(srf, x, y);
443 }
444
445 const TileType &Planet::TileTypeAt(const glm::dvec3 &p) const noexcept {
446         return GetSimulation().TileTypes()[TileAt(p).type];
447 }
448
449 Tile &Planet::TileAt(int surface, int x, int y) noexcept {
450         return tiles[IndexOf(surface, x, y)];
451 }
452
453 const Tile &Planet::TileAt(int surface, int x, int y) const noexcept {
454         return tiles[IndexOf(surface, x, y)];
455 }
456
457 const TileType &Planet::TypeAt(int srf, int x, int y) const noexcept {
458         return GetSimulation().TileTypes()[TileAt(srf, x, y).type];
459 }
460
461 glm::dvec3 Planet::TileCenter(int srf, int x, int y, double e) const noexcept {
462         double u = (double(x) - Radius() + 0.5) / Radius();
463         double v = (double(y) - Radius() + 0.5) / Radius();
464         return glm::normalize(cubeunmap(srf, u, v)) * (Radius() + e);
465 }
466
467 void Planet::BuildVAO(const Set<TileType> &ts) {
468         vao.reset(new graphics::SimpleVAO<Attributes, unsigned int>);
469         vao->Bind();
470         vao->BindAttributes();
471         vao->EnableAttribute(0);
472         vao->EnableAttribute(1);
473         vao->EnableAttribute(2);
474         vao->AttributePointer<glm::vec3>(0, false, offsetof(Attributes, position));
475         vao->AttributePointer<glm::vec3>(1, false, offsetof(Attributes, normal));
476         vao->AttributePointer<glm::vec3>(2, false, offsetof(Attributes, tex_coord));
477         vao->ReserveAttributes(TilesTotal() * 4, GL_STATIC_DRAW);
478         {
479                 auto attrib = vao->MapAttributes(GL_WRITE_ONLY);
480                 float offset = Radius();
481
482                 // srf  0  1  2  3  4  5
483                 //  up +Z +X +Y -Z -X -Y
484
485                 for (int index = 0, surface = 0; surface < 6; ++surface) {
486                         for (int y = 0; y < sidelength; ++y) {
487                                 for (int x = 0; x < sidelength; ++x, ++index) {
488                                         glm::vec3 pos[4];
489                                         pos[0][(surface + 0) % 3] = float(x + 0) - offset;
490                                         pos[0][(surface + 1) % 3] = float(y + 0) - offset;
491                                         pos[0][(surface + 2) % 3] = offset;
492                                         pos[1][(surface + 0) % 3] = float(x + 0) - offset;
493                                         pos[1][(surface + 1) % 3] = float(y + 1) - offset;
494                                         pos[1][(surface + 2) % 3] = offset;
495                                         pos[2][(surface + 0) % 3] = float(x + 1) - offset;
496                                         pos[2][(surface + 1) % 3] = float(y + 0) - offset;
497                                         pos[2][(surface + 2) % 3] = offset;
498                                         pos[3][(surface + 0) % 3] = float(x + 1) - offset;
499                                         pos[3][(surface + 1) % 3] = float(y + 1) - offset;
500                                         pos[3][(surface + 2) % 3] = offset;
501
502                                         float tex = ts[TileAt(surface, x, y).type].texture;
503                                         const float tex_v_begin = surface < 3 ? 1.0f : 0.0f;
504                                         const float tex_v_end = surface < 3 ? 0.0f : 1.0f;
505
506                                         attrib[4 * index + 0].position = glm::normalize(pos[0]) * (surface < 3 ? offset : -offset);
507                                         attrib[4 * index + 0].normal = pos[0];
508                                         attrib[4 * index + 0].tex_coord[0] = 0.0f;
509                                         attrib[4 * index + 0].tex_coord[1] = tex_v_begin;
510                                         attrib[4 * index + 0].tex_coord[2] = tex;
511
512                                         attrib[4 * index + 1].position = glm::normalize(pos[1]) * (surface < 3 ? offset : -offset);
513                                         attrib[4 * index + 1].normal = pos[1];
514                                         attrib[4 * index + 1].tex_coord[0] = 0.0f;
515                                         attrib[4 * index + 1].tex_coord[1] = tex_v_end;
516                                         attrib[4 * index + 1].tex_coord[2] = tex;
517
518                                         attrib[4 * index + 2].position = glm::normalize(pos[2]) * (surface < 3 ? offset : -offset);
519                                         attrib[4 * index + 2].normal = pos[2];
520                                         attrib[4 * index + 2].tex_coord[0] = 1.0f;
521                                         attrib[4 * index + 2].tex_coord[1] = tex_v_begin;
522                                         attrib[4 * index + 2].tex_coord[2] = tex;
523
524                                         attrib[4 * index + 3].position = glm::normalize(pos[3]) * (surface < 3 ? offset : -offset);
525                                         attrib[4 * index + 3].normal = pos[3];
526                                         attrib[4 * index + 3].tex_coord[0] = 1.0f;
527                                         attrib[4 * index + 3].tex_coord[1] = tex_v_end;
528                                         attrib[4 * index + 3].tex_coord[2] = tex;
529                                 }
530                         }
531                 }
532         }
533         vao->BindElements();
534         vao->ReserveElements(TilesTotal() * 6, GL_STATIC_DRAW);
535         {
536                 auto element = vao->MapElements(GL_WRITE_ONLY);
537                 int index = 0;
538                 for (int surface = 0; surface < 3; ++surface) {
539                         for (int y = 0; y < sidelength; ++y) {
540                                 for (int x = 0; x < sidelength; ++x, ++index) {
541                                         element[6 * index + 0] = 4 * index + 0;
542                                         element[6 * index + 1] = 4 * index + 2;
543                                         element[6 * index + 2] = 4 * index + 1;
544                                         element[6 * index + 3] = 4 * index + 1;
545                                         element[6 * index + 4] = 4 * index + 2;
546                                         element[6 * index + 5] = 4 * index + 3;
547                                 }
548                         }
549                 }
550                 for (int surface = 3; surface < 6; ++surface) {
551                         for (int y = 0; y < sidelength; ++y) {
552                                 for (int x = 0; x < sidelength; ++x, ++index) {
553                                         element[6 * index + 0] = 4 * index + 0;
554                                         element[6 * index + 1] = 4 * index + 1;
555                                         element[6 * index + 2] = 4 * index + 2;
556                                         element[6 * index + 3] = 4 * index + 2;
557                                         element[6 * index + 4] = 4 * index + 1;
558                                         element[6 * index + 5] = 4 * index + 3;
559                                 }
560                         }
561                 }
562         }
563         vao->Unbind();
564 }
565
566 void Planet::Draw(app::Assets &assets, graphics::Viewport &viewport) {
567         if (!vao) return;
568
569         vao->Bind();
570         vao->DrawTriangles(TilesTotal() * 6);
571 }
572
573
574 void GenerateEarthlike(const Set<TileType> &tiles, Planet &p) noexcept {
575         math::SimplexNoise elevation_gen(0);
576         math::SimplexNoise variation_gen(45623752346);
577
578         const int ice = tiles["ice"].id;
579         const int ocean = tiles["ocean"].id;
580         const int water = tiles["water"].id;
581         const int sand = tiles["sand"].id;
582         const int grass = tiles["grass"].id;
583         const int tundra = tiles["tundra"].id;
584         const int taiga = tiles["taiga"].id;
585         const int desert = tiles["desert"].id;
586         const int mntn = tiles["mountain"].id;
587         const int algae = tiles["algae"].id;
588         const int forest = tiles["forest"].id;
589         const int jungle = tiles["jungle"].id;
590         const int rock = tiles["rock"].id;
591         const int wheat = tiles["wheat"].id;
592
593         constexpr double ocean_thresh = -0.2;
594         constexpr double water_thresh = 0.0;
595         constexpr double beach_thresh = 0.05;
596         constexpr double highland_thresh = 0.4;
597         constexpr double mountain_thresh = 0.5;
598
599         const glm::dvec3 axis(glm::dvec4(0.0, 1.0, 0.0, 0.0) * glm::eulerAngleXY(p.SurfaceTilt().x, p.SurfaceTilt().y));
600         const double cap_thresh = std::abs(std::cos(p.AxialTilt().x));
601         const double equ_thresh = std::abs(std::sin(p.AxialTilt().x)) / 2.0;
602         const double fzone_start = equ_thresh - (equ_thresh - cap_thresh) / 3.0;
603         const double fzone_end = cap_thresh + (equ_thresh - cap_thresh) / 3.0;
604
605         for (int surface = 0; surface <= 5; ++surface) {
606                 for (int y = 0; y < p.SideLength(); ++y) {
607                         for (int x = 0; x < p.SideLength(); ++x) {
608                                 glm::dvec3 to_tile = p.TileCenter(surface, x, y);
609                                 double near_axis = std::abs(glm::dot(glm::normalize(to_tile), axis));
610                                 if (near_axis > cap_thresh) {
611                                         p.TileAt(surface, x, y).type = ice;
612                                         continue;
613                                 }
614                                 float elevation = math::OctaveNoise(
615                                         elevation_gen,
616                                         glm::vec3(to_tile / p.Radius()),
617                                         3,   // octaves
618                                         0.5, // persistence
619                                         5 / p.Radius(), // frequency
620                                         2,   // amplitude
621                                         2    // growth
622                                 );
623                                 float variation = math::OctaveNoise(
624                                         variation_gen,
625                                         glm::vec3(to_tile / p.Radius()),
626                                         3,   // octaves
627                                         0.5, // persistence
628                                         16 / p.Radius(), // frequency
629                                         2,   // amplitude
630                                         2    // growth
631                                 );
632                                 if (elevation < ocean_thresh) {
633                                         p.TileAt(surface, x, y).type = ocean;
634                                 } else if (elevation < water_thresh) {
635                                         if (variation > 0.3) {
636                                                 p.TileAt(surface, x, y).type = algae;
637                                         } else {
638                                                 p.TileAt(surface, x, y).type = water;
639                                         }
640                                 } else if (elevation < beach_thresh) {
641                                         p.TileAt(surface, x, y).type = sand;
642                                 } else if (elevation < highland_thresh) {
643                                         if (near_axis < equ_thresh) {
644                                                 if (variation > 0.6) {
645                                                         p.TileAt(surface, x, y).type = grass;
646                                                 } else if (variation > 0.2) {
647                                                         p.TileAt(surface, x, y).type = sand;
648                                                 } else {
649                                                         p.TileAt(surface, x, y).type = desert;
650                                                 }
651                                         } else if (near_axis < fzone_start) {
652                                                 if (variation > 0.4) {
653                                                         p.TileAt(surface, x, y).type = forest;
654                                                 } else if (variation < -0.5) {
655                                                         p.TileAt(surface, x, y).type = jungle;
656                                                 } else if (variation > -0.02 && variation < 0.02) {
657                                                         p.TileAt(surface, x, y).type = wheat;
658                                                 } else {
659                                                         p.TileAt(surface, x, y).type = grass;
660                                                 }
661                                         } else if (near_axis < fzone_end) {
662                                                 p.TileAt(surface, x, y).type = tundra;
663                                         } else {
664                                                 p.TileAt(surface, x, y).type = taiga;
665                                         }
666                                 } else if (elevation < mountain_thresh) {
667                                         if (variation > 0.3) {
668                                                 p.TileAt(surface, x, y).type = mntn;
669                                         } else {
670                                                 p.TileAt(surface, x, y).type = rock;
671                                         }
672                                 } else {
673                                         p.TileAt(surface, x, y).type = mntn;
674                                 }
675                         }
676                 }
677         }
678         p.BuildVAO(tiles);
679 }
680
681 void GenerateTest(const Set<TileType> &tiles, Planet &p) noexcept {
682         for (int surface = 0; surface <= 5; ++surface) {
683                 for (int y = 0; y < p.SideLength(); ++y) {
684                         for (int x = 0; x < p.SideLength(); ++x) {
685                                 if (x == p.SideLength() / 2 && y == p.SideLength() / 2) {
686                                         p.TileAt(surface, x, y).type = surface;
687                                 } else {
688                                         p.TileAt(surface, x, y).type = (x == p.SideLength()/2) + (y == p.SideLength()/2) + 6;
689                                 }
690                         }
691                 }
692         }
693         p.BuildVAO(tiles);
694 }
695
696
697 Sun::Sun()
698 : Body()
699 , color(1.0)
700 , luminosity(1.0) {
701 }
702
703 Sun::~Sun() {
704 }
705
706
707 std::vector<TileType::Yield>::const_iterator TileType::FindResource(int r) const {
708         auto yield = resources.cbegin();
709         for (; yield != resources.cend(); ++yield) {
710                 if (yield->resource == r) {
711                         break;
712                 }
713         }
714         return yield;
715 }
716
717 std::vector<TileType::Yield>::const_iterator TileType::FindBestResource(const creature::Composition &comp) const {
718         auto best = resources.cend();
719         double best_value = 0.0;
720         for (auto yield = resources.cbegin(); yield != resources.cend(); ++yield) {
721                 double value = comp.Get(yield->resource);
722                 if (value > best_value) {
723                         best = yield;
724                         best_value = value;
725                 }
726         }
727         return best;
728 }
729
730 }
731 }