]> git.localhorst.tv Git - blobs.git/commitdiff
little better seek ai
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Sat, 25 Nov 2017 12:11:19 +0000 (13:11 +0100)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Sat, 25 Nov 2017 12:11:19 +0000 (13:11 +0100)
15 files changed:
src/app/app.cpp
src/blobs.cpp
src/creature/Creature.hpp
src/creature/Goal.hpp
src/creature/IngestNeed.hpp
src/creature/LocateResourceGoal.hpp
src/creature/creature.cpp
src/creature/goal.cpp
src/creature/need.cpp
src/graphics/shader.cpp
src/world/Planet.hpp
src/world/world.cpp
tst/assert.hpp
tst/world/PlanetTest.cpp [new file with mode: 0644]
tst/world/PlanetTest.hpp [new file with mode: 0644]

index b4ede47f0db1d6bfc96a692da7018ccd45869502..e4dc1a58eef0de9166254142f5ea19073810c9e3 100644 (file)
@@ -211,6 +211,7 @@ Assets::Assets()
        LoadTileTexture("tundra",   textures.tiles, 11);
        LoadTileTexture("water",    textures.tiles, 12);
        LoadTileTexture("wheat",    textures.tiles, 13);
+       textures.tiles.FilterTrilinear();
 
        textures.skins.Bind();
        textures.skins.Reserve(256, 256, 9, format);
@@ -223,6 +224,7 @@ Assets::Assets()
        LoadSkinTexture("7", textures.skins, 6);
        LoadSkinTexture("8", textures.skins, 7);
        LoadSkinTexture("9", textures.skins, 8);
+       textures.skins.FilterTrilinear();
 }
 
 Assets::~Assets() {
index 18e62aad75e82d10e6850b45ab81321504ec8bf1..006644b4342f9309342018c885508cd072519562 100644 (file)
@@ -67,7 +67,7 @@ int main(int argc, char *argv[]) {
        std::cout << "moon cycle in days: " << (moon.OrbitalPeriod() / planet.RotationalPeriod()) << std::endl;
        std::cout << "moon cycles per year: " << (planet.OrbitalPeriod() / moon.OrbitalPeriod()) << std::endl;
 
-       auto blob = new creature::Creature;
+       auto blob = new creature::Creature(sim);
        Spawn(*blob, planet, assets);
        blob->BuildVAO();
        blob->Name("Blob");
@@ -80,7 +80,7 @@ int main(int argc, char *argv[]) {
                // sunset
                //.FirstPerson(3, glm::vec3(0.0f, 0.0f, 0.1f), glm::vec3(1.0f, -0.75f, 0.1f))
                // from afar
-               .MapView(0, glm::vec3(0.0f, 0.0f, 30.0f), 0.0f)
+               .MapView(0, glm::vec3(0.0f, 0.0f, 31.0f), 0.0f)
                // from afar, rotating
                //.Orbital(glm::vec3(-60.0f, 0.0f, 0.0f))
        ;
index aaeb6cfb544387a654d5017483ee000caed1b10a..ce56dfbd56b072bd0bff1c62a1efebf09f423c7f 100644 (file)
@@ -23,13 +23,14 @@ namespace graphics {
 namespace world {
        class Body;
        class Planet;
+       class Simulation;
 }
 namespace creature {
 
 class Creature {
 
 public:
-       Creature();
+       explicit Creature(world::Simulation &);
        ~Creature();
 
        Creature(const Creature &) = delete;
@@ -39,9 +40,15 @@ public:
        Creature &operator =(Creature &&) = delete;
 
 public:
+       world::Simulation &GetSimulation() noexcept { return sim; }
+       const world::Simulation &GetSimulation() const noexcept { return sim; }
+
        void Name(const std::string &n) noexcept { name = n; }
        const std::string &Name() const noexcept { return name; }
 
+       void Size(double s) noexcept { size = s; }
+       double Size() const noexcept { return size; }
+
        void Health(double h) noexcept { health = h; }
        double Health() const noexcept { return health; }
        void Hurt(double d) noexcept;
@@ -62,6 +69,7 @@ public:
 
        void Velocity(const glm::dvec3 &v) noexcept { vel = v; }
        const glm::dvec3 &Velocity() const noexcept { return vel; }
+       bool Moving() const noexcept { return !allzero(vel); }
 
        glm::dmat4 LocalTransform() noexcept;
 
@@ -69,7 +77,9 @@ public:
        void Draw(app::Assets &, graphics::Viewport &);
 
 private:
+       world::Simulation &sim;
        std::string name;
+       double size;
        double health;
 
        std::vector<std::unique_ptr<Need>> needs;
index d664e6b7815509cdec12037582bdac2a5f7cc78d..2ec03bf255e7baa23f792e7d52a3188364d2e6f5 100644 (file)
@@ -1,18 +1,34 @@
 #ifndef BLOBS_CREATURE_GOAL_HPP_
 #define BLOBS_CREATURE_GOAL_HPP_
 
+#include <functional>
+#include <string>
+
+
 namespace blobs {
 namespace creature {
 
 class Creature;
+class Situation;
+class Steering;
 
 class Goal {
 
 public:
-       Goal();
+       using Callback = std::function<void(Goal &)>;
+
+public:
+       explicit Goal(Creature &);
        virtual ~Goal() noexcept;
 
 public:
+       Creature &GetCreature() noexcept { return c; }
+       const Creature &GetCreature() const noexcept { return c; }
+       Situation &GetSituation() noexcept;
+       const Situation &GetSituation() const noexcept;
+       Steering &GetSteering() noexcept;
+       const Steering &GetSteering() const noexcept;
+
        double Urgency() const noexcept { return urgency; }
        void Urgency(double u) noexcept { urgency = u; }
 
@@ -20,14 +36,19 @@ public:
        void Interruptible(bool i) noexcept { interruptible = i; }
 
        bool Complete() const noexcept { return complete; }
-       void Complete(bool i) noexcept { complete = i; }
+       void SetComplete() noexcept;
+       /// only supports one callback for now, new one will replace an old
+       void OnComplete(Callback) noexcept;
 
 public:
-       virtual void Enable(Creature &) { }
+       virtual std::string Describe() const = 0;
+       virtual void Enable() { }
        virtual void Tick(double dt) { }
-       virtual void Action(Creature &) { }
+       virtual void Action() { }
 
 private:
+       Creature &c;
+       Callback on_complete;
        double urgency;
        bool interruptible;
        bool complete;
index 4244293a139dc3e2362b0e9c22ba7ab258daeb44..b0262b862fbdd930f31fd1415a5e46255cf0e261 100644 (file)
@@ -7,7 +7,7 @@
 namespace blobs {
 namespace creature {
 
-class Need;
+class Goal;
 
 class IngestNeed
 : public Need {
@@ -20,11 +20,14 @@ public:
        void ApplyEffect(Creature &, double dt) override;
 
 private:
+       void OnLocateComplete(Goal &);
+
+private:
+       Goal *locate_goal;
        int resource;
        double speed;
        double damage;
        bool ingesting;
-       bool locating;
 
 };
 
index b5d390c7c69e71e9bdd519e90529fbd786ff0eae..afa5b93d00ee6ef8d0b364cd4988c03d716803f1 100644 (file)
@@ -1,6 +1,9 @@
 #ifndef BLOBS_CREATURE_LOCATERESOURCEGOAL_HPP_
 #define BLOBS_CREATURE_LOCATERESOURCEGOAL_HPP_
 
+#include "../graphics/glm.hpp"
+
+
 namespace blobs {
 namespace creature {
 
@@ -8,16 +11,26 @@ class LocateResourceGoal
 : public Goal {
 
 public:
-       explicit LocateResourceGoal(int resource);
+       LocateResourceGoal(Creature &, int resource);
        ~LocateResourceGoal() noexcept override;
 
 public:
-       void Enable(Creature &) override;
+       std::string Describe() const override;
+       void Enable() override;
        void Tick(double dt) override;
-       void Action(Creature &) override;
+       void Action() override;
+
+private:
+       void LocateResource();
+       void SearchVicinity();
+       bool OnTargetTile() const noexcept;
 
 private:
        int res;
+       bool found;
+       glm::dvec3 target_pos;
+       int target_srf;
+       glm::ivec2 target_tile;
 
 };
 
index 1278e267135f4748fd125e9ddef6fe5844bb2ba5..043aed7353cd66d4c2d6fbfff8eaf8e830f25031 100644 (file)
 #include <glm/gtx/transform.hpp>
 
 #include <iostream>
+#include <glm/gtx/io.hpp>
 
 
 namespace blobs {
 namespace creature {
 
-Creature::Creature()
-: name()
+Creature::Creature(world::Simulation &sim)
+: sim(sim)
+, name()
+, size(1.0)
 , health(1.0)
 , needs()
 , goals()
@@ -40,7 +43,8 @@ void Creature::Hurt(double dt) noexcept {
 }
 
 void Creature::AddGoal(std::unique_ptr<Goal> &&g) {
-       g->Enable(*this);
+       std::cout << "new goal: " << g->Describe() << std::endl;
+       g->Enable();
        goals.emplace_back(std::move(g));
 }
 
@@ -73,11 +77,18 @@ void Creature::Tick(double dt) {
        }
        // if active goal can be interrupted, check priorities
        if (goals.size() > 1 && goals[0]->Interruptible()) {
+               Goal *old_top = &*goals[0];
                std::sort(goals.begin(), goals.end(), GoalCompare);
+               Goal *new_top = &*goals[0];
+               if (new_top != old_top) {
+                       std::cout << "changing goal from " << old_top->Describe()
+                               << " to " << new_top->Describe() << std::endl;
+               }
        }
-       goals[0]->Action(*this);
+       goals[0]->Action();
        for (auto goal = goals.begin(); goal != goals.end();) {
                if ((*goal)->Complete()) {
+                       std::cout << "complete goal: " << (*goal)->Describe() << std::endl;
                        goals.erase(goal);
                } else {
                        ++goal;
@@ -87,10 +98,10 @@ void Creature::Tick(double dt) {
 
 glm::dmat4 Creature::LocalTransform() noexcept {
        // TODO: surface transform
-       constexpr double half_height = 0.25;
+       const double half_size = size * 0.5;
        const glm::dvec3 &pos = situation.Position();
-       return glm::translate(glm::dvec3(pos.x, pos.y, pos.z + situation.GetPlanet().Radius() + half_height))
-               * glm::scale(glm::dvec3(half_height, half_height, half_height));
+       return glm::translate(glm::dvec3(pos.x, pos.y, pos.z + half_size))
+               * glm::scale(glm::dvec3(half_size, half_size, half_size));
 }
 
 void Creature::BuildVAO() {
@@ -183,7 +194,8 @@ void Creature::Draw(app::Assets &assets, graphics::Viewport &viewport) {
 
 void Spawn(Creature &c, world::Planet &p, app::Assets &assets) {
        p.AddCreature(&c);
-       c.GetSituation().SetPlanetSurface(p, 0, glm::dvec3(0.0, 0.0, 0.0));
+       c.GetSituation().SetPlanetSurface(p, 0, p.TileCenter(0, p.SideLength() / 2, p.SideLength() / 2));
+       c.Size(0.5);
 
        // probe surrounding area for common resources
        int start = p.SideLength() / 2 - 2;
@@ -224,7 +236,7 @@ void Spawn(Creature &c, world::Planet &p, app::Assets &assets) {
                std::cout << "require drinking " << assets.data.resources[liquid].label << std::endl;
                std::unique_ptr<Need> need(new IngestNeed(liquid, 0.2, 0.01));
                need->name = assets.data.resources[liquid].label;
-               need->gain = 0.01;
+               need->gain = 0.02;
                need->inconvenient = 0.6;
                need->critical = 0.95;
                c.AddNeed(std::move(need));
@@ -233,7 +245,7 @@ void Spawn(Creature &c, world::Planet &p, app::Assets &assets) {
                std::cout << "require eating " << assets.data.resources[solid].label << std::endl;
                std::unique_ptr<Need> need(new IngestNeed(solid, 0.1, 0.001));
                need->name = assets.data.resources[solid].label;
-               need->gain = 0.007;
+               need->gain = 0.017;
                need->inconvenient = 0.6;
                need->critical = 0.95;
                c.AddNeed(std::move(need));
@@ -274,7 +286,11 @@ void Situation::Move(const glm::dvec3 &dp) noexcept {
        position += dp;
        if (OnSurface()) {
                // enforce ground constraint
-               position.z = std::max(0.0, position.z);
+               if (Surface() < 3) {
+                       position[(Surface() + 2) % 3] = std::max(0.0, position[(Surface() + 2) % 3]);
+               } else {
+                       position[(Surface() + 2) % 3] = std::min(0.0, position[(Surface() + 2) % 3]);
+               }
        }
 }
 
index 1b9d5d07fd0bef94038e3332dadad640320bd68f..18714e49cb195637d82ae1abfc0824e756b962ea 100644 (file)
@@ -3,13 +3,21 @@
 
 #include "Creature.hpp"
 #include "../world/Planet.hpp"
+#include "../world/Resource.hpp"
+#include "../world/Simulation.hpp"
 #include "../world/TileType.hpp"
 
+#include <iostream>
+#include <glm/gtx/io.hpp>
+
+
 namespace blobs {
 namespace creature {
 
-Goal::Goal()
-: urgency(0.0)
+Goal::Goal(Creature &c)
+: c(c)
+, on_complete()
+, urgency(0.0)
 , interruptible(true)
 , complete(false) {
 }
@@ -17,68 +25,145 @@ Goal::Goal()
 Goal::~Goal() noexcept {
 }
 
+Situation &Goal::GetSituation() noexcept {
+       return c.GetSituation();
+}
+
+const Situation &Goal::GetSituation() const noexcept {
+       return c.GetSituation();
+}
+
+Steering &Goal::GetSteering() noexcept {
+       return c.GetSteering();
+}
 
-LocateResourceGoal::LocateResourceGoal(int res)
-: res(res) {
+const Steering &Goal::GetSteering() const noexcept {
+       return c.GetSteering();
+}
+
+void Goal::SetComplete() noexcept {
+       if (!complete) {
+               complete = true;
+               if (on_complete) {
+                       on_complete(*this);
+               }
+       }
+}
+
+void Goal::OnComplete(std::function<void(Goal &)> cb) noexcept {
+       on_complete = cb;
+       if (complete) {
+               on_complete(*this);
+       }
+}
+
+
+LocateResourceGoal::LocateResourceGoal(Creature &c, int res)
+: Goal(c)
+, res(res)
+, found(false)
+, target_pos(0.0)
+, target_srf(0)
+, target_tile(0) {
 }
 
 LocateResourceGoal::~LocateResourceGoal() noexcept {
 }
 
-void LocateResourceGoal::Enable(Creature &c) {
+std::string LocateResourceGoal::Describe() const {
+       return "locate " + GetCreature().GetSimulation().Resources()[res].name;
+}
+
+void LocateResourceGoal::Enable() {
+       LocateResource();
 }
 
 void LocateResourceGoal::Tick(double dt) {
 }
 
-void LocateResourceGoal::Action(Creature &c) {
-       if (c.GetSituation().OnSurface()) {
-               const world::TileType &t = c.GetSituation().GetTileType();
+void LocateResourceGoal::Action() {
+       if (!found) {
+               LocateResource();
+       } else if (OnTargetTile()) {
+               GetSteering().Halt();
+               if (!GetCreature().Moving()) {
+                       SetComplete();
+               }
+       } else {
+               GetSteering().GoTo(target_pos);
+       }
+}
+
+void LocateResourceGoal::LocateResource() {
+       if (GetSituation().OnSurface()) {
+               const world::TileType &t = GetSituation().GetTileType();
                auto yield = t.FindResource(res);
                if (yield != t.resources.cend()) {
                        // hoooray
-                       c.GetSteering().Halt();
-                       Complete(true);
+                       GetSteering().Halt();
+                       found = true;
+                       target_pos = GetSituation().Position();
+                       target_srf = GetSituation().Surface();
+                       target_tile = GetSituation().GetPlanet().SurfacePosition(target_srf, target_pos);
                } else {
                        // go find somewhere else
-                       const world::Planet &planet = c.GetSituation().GetPlanet();
-                       double side_length = planet.SideLength();
-                       double offset = side_length * 0.5;
-                       glm::ivec2 loc = glm::ivec2(c.GetSituation().Position() + offset);
-                       glm::ivec2 seek_radius(2);
-                       glm::ivec2 begin(glm::max(glm::ivec2(0), loc - seek_radius));
-                       glm::ivec2 end(glm::min(glm::ivec2(side_length), loc + seek_radius));
-                       const world::TileType::Yield *best = nullptr;
-                       glm::ivec2 best_pos;
-                       double best_distance = 2 * side_length * side_length;
-                       for (int y = begin.y; y < end.y; ++y) {
-                               for (int x = begin.x; x < end.x; ++x) {
-                                       const world::TileType &type = planet.TypeAt(c.GetSituation().Surface(), x, y);
-                                       auto yield = type.FindResource(res);
-                                       if (yield != type.resources.cend()) {
-                                               double dist = glm::length2(glm::dvec3(x - offset + 0.5, y - offset + 0.5, 0.0) - c.GetSituation().Position());
-                                               if (!best) {
-                                                       best = &*yield;
-                                                       best_pos = glm::ivec2(x, y);
-                                                       best_distance = dist;
-                                               } else if (yield->ubiquity + (dist * 0.125) > best->ubiquity + (best_distance * 0.125)) {
-                                                       best = &*yield;
-                                                       best_pos = glm::ivec2(x, y);
-                                                       best_distance = dist;
-                                               }
-                                       }
+                       SearchVicinity();
+               }
+       } else {
+               // well, what now?
+       }
+}
+
+void LocateResourceGoal::SearchVicinity() {
+       const world::Planet &planet = GetSituation().GetPlanet();
+       int srf = GetSituation().Surface();
+       const glm::dvec3 &pos = GetSituation().Position();
+
+       glm::ivec2 loc = planet.SurfacePosition(srf, pos);
+       glm::ivec2 seek_radius(2);
+       glm::ivec2 begin(glm::max(glm::ivec2(0), loc - seek_radius));
+       glm::ivec2 end(glm::min(glm::ivec2(planet.SideLength()), loc + seek_radius));
+
+       const world::TileType::Yield *best = nullptr;
+       glm::ivec2 best_pos;
+       double best_distance;
+
+       for (int y = begin.y; y < end.y; ++y) {
+               for (int x = begin.x; x < end.x; ++x) {
+                       const world::TileType &type = planet.TypeAt(srf, x, y);
+                       auto yield = type.FindResource(res);
+                       if (yield != type.resources.cend()) {
+                               double dist = glm::length2(planet.TileCenter(srf, x, y) - pos);
+                               if (!best) {
+                                       best = &*yield;
+                                       best_pos = glm::ivec2(x, y);
+                                       best_distance = dist;
+                               } else if (yield->ubiquity - (dist * 0.125) > best->ubiquity - (best_distance * 0.125)) {
+                                       best = &*yield;
+                                       best_pos = glm::ivec2(x, y);
+                                       best_distance = dist;
                                }
                        }
-                       if (best) {
-                               c.GetSteering().GoTo(glm::dvec3(best_pos.x - offset + 0.5, best_pos.y - offset + 0.5, 0.0));
-                       } else {
-                               // oh crap
-                       }
                }
+       }
+       if (best) {
+               found = true;
+               target_pos = planet.TileCenter(srf, best_pos.x, best_pos.y);
+               target_srf = srf;
+               target_tile = best_pos;
+               GetSteering().GoTo(target_pos);
+               std::cout << "found resource at " << target_pos << std::endl;
        } else {
-               // well, what now?
+               // oh crap
        }
 }
 
+bool LocateResourceGoal::OnTargetTile() const noexcept {
+       const Situation &s = GetSituation();
+       return s.OnSurface()
+               && s.Surface() == target_srf
+               && s.GetPlanet().SurfacePosition(s.Surface(), s.Position()) == target_tile;
+}
+
 }
 }
index 60e7cb816b3a0b8cd375df295da00e80723dbc3a..64ebf5bb060d80a7384aa8503acd93ae9b9337f7 100644 (file)
@@ -28,11 +28,11 @@ void Need::Decrease(double delta) noexcept {
 
 
 IngestNeed::IngestNeed(int resource, double speed, double damage)
-: resource(resource)
+: locate_goal(nullptr)
+, resource(resource)
 , speed(speed)
 , damage(damage)
-, ingesting(false)
-, locating(false) {
+, ingesting(false) {
 }
 
 IngestNeed::~IngestNeed() {
@@ -53,20 +53,33 @@ void IngestNeed::ApplyEffect(Creature &c, double dt) {
                                        Decrease(std::min(yield.ubiquity, speed) * dt);
                                        if (value == 0.0) {
                                                ingesting = false;
-                                               locating = false;
+                                               if (locate_goal) {
+                                                       // abort
+                                                       locate_goal->Complete();
+                                               }
                                        }
                                        break;
                                }
                        }
-                       if (!found && !locating) {
-                               c.AddGoal(std::unique_ptr<Goal>(new LocateResourceGoal(resource)));
-                               locating = true;
+                       if (!found && !locate_goal) {
+                               locate_goal = new LocateResourceGoal(c, resource);
+                               locate_goal->OnComplete([&](Goal &g){ OnLocateComplete(g); });
+                               c.AddGoal(std::unique_ptr<Goal>(locate_goal));
                        }
                }
        }
        if (IsCritical()) {
                c.Hurt(damage * dt);
        }
+       if (locate_goal) {
+               locate_goal->Urgency(value);
+       }
+}
+
+void IngestNeed::OnLocateComplete(Goal &g) {
+       if (&g == locate_goal) {
+               locate_goal = nullptr;
+       }
 }
 
 
index c63a28007b7bd312957cd782efbf42a38e24601b..1b641b07107d8f2954dca055c7e249e3c3b410fa 100644 (file)
@@ -227,7 +227,7 @@ PlanetSurface::PlanetSurface()
 
                "void main() {\n"
                        "vec3 tex_color = texture(tex_sampler, frag_tex_uv).rgb;\n"
-                       "vec3 total_light = tex_color * vec3(0.01, 0.01, 0.01);\n"
+                       "vec3 total_light = tex_color * vec3(0.1, 0.1, 0.1);\n"
                        "for (int i = 0; i < num_lights; ++i) {\n"
                                "vec3 to_light = light[i].position - vtx_viewspace;\n"
                                "float distance = length(to_light) + length(vtx_viewspace);\n"
index 8cd9efef5233b81cedb6a751622ca827af31f4b8..2274d045aed749e9d4b65a4f0eceb075b508db34 100644 (file)
@@ -9,6 +9,7 @@
 #include "../graphics/SimpleVAO.hpp"
 
 #include <cassert>
+#include <memory>
 #include <vector>
 #include <GL/glew.h>
 
@@ -47,8 +48,8 @@ public:
        /// Convert coordinates into a tile index.
        int IndexOf(int surface, int x, int y) const {
                assert(0 <= surface && surface <= 5);
-               assert(0 <= x && x <= sidelength);
-               assert(0 <= y && y <= sidelength);
+               assert(0 <= x && x < sidelength);
+               assert(0 <= y && y < sidelength);
                return surface * TilesPerSurface() + y * SideLength() + x;
        }
        /// The length of the side of each surface.
@@ -64,7 +65,15 @@ public:
                return 6 * TilesPerSurface();
        }
 
-       glm::dvec3 TileCenter(int surface, int x, int y) const noexcept;
+       double TileToPosition(int t) const noexcept { return double(t) - Radius(); }
+       int PositionToTile(double p) const noexcept { return int(p + Radius()); }
+
+       // tile coordinates of position on surface
+       glm::ivec2 SurfacePosition(int surface, const glm::dvec3 &) const noexcept;
+       // height of point over surface
+       double SurfaceElevation(int surface, const glm::dvec3 &) const noexcept;
+       // center point of tile on surface at elevation
+       glm::dvec3 TileCenter(int surface, int x, int y, double elevation = 0.0) const noexcept;
 
        void BuildVAO(const Set<TileType> &);
        void Draw(app::Assets &, graphics::Viewport &) override;
@@ -77,7 +86,7 @@ private:
                glm::vec3 position;
                glm::vec3 tex_coord;
        };
-       graphics::SimpleVAO<Attributes, unsigned int> vao;
+       std::unique_ptr<graphics::SimpleVAO<Attributes, unsigned int>> vao;
 
 };
 
index 0f9cb1e373d36d1ad8b20e2719b1dbadb5736f5b..0b1260f8673d8a0eb1b054bec81847b61602d711 100644 (file)
@@ -290,28 +290,41 @@ Planet::Planet(int sidelength)
 Planet::~Planet() {
 }
 
-const TileType &Planet::TypeAt(int surface, int x, int y) const {
-       return GetSimulation().TileTypes()[TileAt(surface, x, y).type];
+const TileType &Planet::TypeAt(int srf, int x, int y) const {
+       return GetSimulation().TileTypes()[TileAt(srf, x, y).type];
 }
 
-glm::dvec3 Planet::TileCenter(int surface, int x, int y) const noexcept {
+glm::ivec2 Planet::SurfacePosition(int srf, const glm::dvec3 &pos) const noexcept {
+       return glm::ivec2(
+               PositionToTile(pos[(srf + 0) % 3]),
+               PositionToTile(pos[(srf + 1) % 3]));
+}
+
+double Planet::SurfaceElevation(int srf, const glm::dvec3 &pos) const noexcept {
+       return srf < 3
+               ? pos[(srf + 2) % 3] - Radius()
+               : -pos[(srf + 2) % 3] - Radius();
+}
+
+glm::dvec3 Planet::TileCenter(int srf, int x, int y, double e) const noexcept {
        glm::dvec3 center(0.0f);
-       center[(surface + 0) % 3] = x + 0.5 - Radius();
-       center[(surface + 1) % 3] = y + 0.5 - Radius();
-       center[(surface + 2) % 3] = surface < 3 ? Radius() : -Radius();
+       center[(srf + 0) % 3] = x + 0.5 - Radius();
+       center[(srf + 1) % 3] = y + 0.5 - Radius();
+       center[(srf + 2) % 3] = srf < 3 ? (Radius() + e) : -(Radius() + e);
        return center;
 }
 
 void Planet::BuildVAO(const Set<TileType> &ts) {
-       vao.Bind();
-       vao.BindAttributes();
-       vao.EnableAttribute(0);
-       vao.EnableAttribute(1);
-       vao.AttributePointer<glm::vec3>(0, false, offsetof(Attributes, position));
-       vao.AttributePointer<glm::vec3>(1, false, offsetof(Attributes, tex_coord));
-       vao.ReserveAttributes(TilesTotal() * 4, GL_STATIC_DRAW);
+       vao.reset(new graphics::SimpleVAO<Attributes, unsigned int>);
+       vao->Bind();
+       vao->BindAttributes();
+       vao->EnableAttribute(0);
+       vao->EnableAttribute(1);
+       vao->AttributePointer<glm::vec3>(0, false, offsetof(Attributes, position));
+       vao->AttributePointer<glm::vec3>(1, false, offsetof(Attributes, tex_coord));
+       vao->ReserveAttributes(TilesTotal() * 4, GL_STATIC_DRAW);
        {
-               auto attrib = vao.MapAttributes(GL_WRITE_ONLY);
+               auto attrib = vao->MapAttributes(GL_WRITE_ONLY);
                float offset = Radius();
 
                // srf  0  1  2  3  4  5
@@ -354,10 +367,10 @@ void Planet::BuildVAO(const Set<TileType> &ts) {
                        }
                }
        }
-       vao.BindElements();
-       vao.ReserveElements(TilesTotal() * 6, GL_STATIC_DRAW);
+       vao->BindElements();
+       vao->ReserveElements(TilesTotal() * 6, GL_STATIC_DRAW);
        {
-               auto element = vao.MapElements(GL_WRITE_ONLY);
+               auto element = vao->MapElements(GL_WRITE_ONLY);
                int index = 0;
                for (int surface = 0; surface < 3; ++surface) {
                        for (int y = 0; y < sidelength; ++y) {
@@ -384,24 +397,26 @@ void Planet::BuildVAO(const Set<TileType> &ts) {
                        }
                }
        }
-       vao.Unbind();
+       vao->Unbind();
 }
 
 void Planet::Draw(app::Assets &assets, graphics::Viewport &viewport) {
-       vao.Bind();
+       if (!vao) return;
+
+       vao->Bind();
        const glm::mat4 &MV = assets.shaders.planet_surface.MV();
        assets.shaders.planet_surface.SetNormal(glm::vec3(MV * glm::vec4(0.0f, 0.0f, 1.0f, 0.0f)));
-       vao.DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 0);
+       vao->DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 0);
        assets.shaders.planet_surface.SetNormal(glm::vec3(MV * glm::vec4(1.0f, 0.0f, 0.0f, 0.0f)));
-       vao.DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 1);
+       vao->DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 1);
        assets.shaders.planet_surface.SetNormal(glm::vec3(MV * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f)));
-       vao.DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 2);
+       vao->DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 2);
        assets.shaders.planet_surface.SetNormal(glm::vec3(MV * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f)));
-       vao.DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 3);
+       vao->DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 3);
        assets.shaders.planet_surface.SetNormal(glm::vec3(MV * glm::vec4(-1.0f, 0.0f, 0.0f, 0.0f)));
-       vao.DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 4);
+       vao->DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 4);
        assets.shaders.planet_surface.SetNormal(glm::vec3(MV * glm::vec4(0.0f, -1.0f, 0.0f, 0.0f)));
-       vao.DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 5);
+       vao->DrawTriangles(TilesPerSurface() * 6, TilesPerSurface() * 6 * 5);
 }
 
 
index 85e43246f67f339fdeb51bb878660e4156632c2c..5042180915195538e0f5d6d0dde6ccb9e3471cb3 100644 (file)
 namespace blobs {
 namespace test {
 
+template<class T>
+void AssertEqual(
+       const std::string &msg,
+       const glm::tvec2<T> &expected,
+       const glm::tvec2<T> &actual,
+       T epsilon = std::numeric_limits<T>::epsilon()
+) {
+       CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
+               msg + " (X component)",
+               expected.x, actual.x, epsilon
+       );
+       CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
+               msg + " (Y component)",
+               expected.y, actual.y, epsilon
+       );
+}
+
 template<class T>
 void AssertEqual(
        const std::string &msg,
diff --git a/tst/world/PlanetTest.cpp b/tst/world/PlanetTest.cpp
new file mode 100644 (file)
index 0000000..1ab0bf4
--- /dev/null
@@ -0,0 +1,79 @@
+#include "PlanetTest.hpp"
+
+#include "../assert.hpp"
+
+#include "world/Planet.hpp"
+
+#include <limits>
+
+CPPUNIT_TEST_SUITE_REGISTRATION(blobs::world::test::PlanetTest);
+
+using blobs::test::AssertEqual;
+
+
+namespace blobs {
+namespace world {
+namespace test {
+
+void PlanetTest::setUp() {
+}
+
+void PlanetTest::tearDown() {
+}
+
+
+void PlanetTest::testPositionConversion() {
+       Planet p(5);
+
+       CPPUNIT_ASSERT_EQUAL_MESSAGE(
+               "wrong sidelength of planet",
+               5, p.SideLength());
+       CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
+               "wrong radius of planet",
+               2.5, p.Radius(), std::numeric_limits<double>::epsilon());
+
+       CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
+               "wrong scalar conversion",
+               -2.5, p.TileToPosition(0), std::numeric_limits<double>::epsilon());
+       CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
+               "wrong scalar conversion",
+               -0.5, p.TileToPosition(2), std::numeric_limits<double>::epsilon());
+       CPPUNIT_ASSERT_EQUAL_MESSAGE(
+               "wrong scalar conversion",
+               2, p.PositionToTile(0.1));
+       CPPUNIT_ASSERT_EQUAL_MESSAGE(
+               "wrong scalar conversion",
+               2, p.PositionToTile(-0.1));
+       CPPUNIT_ASSERT_EQUAL_MESSAGE(
+               "wrong scalar conversion",
+               1, p.PositionToTile(-0.6));
+       CPPUNIT_ASSERT_EQUAL_MESSAGE(
+               "wrong scalar conversion",
+               4, p.PositionToTile(2.0));
+
+       AssertEqual(
+               "wrong surface position",
+               glm::ivec2(2, 2), p.SurfacePosition(0, glm::dvec3(0.0, 0.0, 2.5))
+       );
+       AssertEqual(
+               "wrong surface position",
+               glm::ivec2(2, 2), p.SurfacePosition(0, glm::dvec3(0.1, 0.1, 2.5))
+       );
+       AssertEqual(
+               "wrong surface position",
+               glm::ivec2(2, 2), p.SurfacePosition(0, glm::dvec3(-0.1, -0.1, 2.5))
+       );
+       AssertEqual(
+               "wrong surface position",
+               glm::ivec2(3, 1), p.SurfacePosition(0, glm::dvec3(1.0, -1.0, 2.5))
+       );
+
+       AssertEqual(
+               "wrong tile center",
+               glm::dvec3(0.0, 0.0, 2.5), p.TileCenter(0, 2, 2)
+       );
+}
+
+}
+}
+}
diff --git a/tst/world/PlanetTest.hpp b/tst/world/PlanetTest.hpp
new file mode 100644 (file)
index 0000000..1fc4b15
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef BLOBS_TEST_WORLD_PLANETTEST_HPP_
+#define BLOBS_TEST_WORLD_PLANETTEST_HPP_
+
+#include <cppunit/extensions/HelperMacros.h>
+
+
+namespace blobs {
+namespace world {
+namespace test {
+
+class PlanetTest
+: public CppUnit::TestFixture {
+
+CPPUNIT_TEST_SUITE(PlanetTest);
+
+CPPUNIT_TEST(testPositionConversion);
+
+CPPUNIT_TEST_SUITE_END();
+
+public:
+       void setUp();
+       void tearDown();
+
+       void testPositionConversion();
+
+};
+
+}
+}
+}
+
+#endif