From 3b0a4ba59825b2d6a47e9d997736742edf55240c Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Sat, 25 Nov 2017 13:11:19 +0100 Subject: [PATCH] little better seek ai --- src/app/app.cpp | 2 + src/blobs.cpp | 4 +- src/creature/Creature.hpp | 12 +- src/creature/Goal.hpp | 29 ++++- src/creature/IngestNeed.hpp | 7 +- src/creature/LocateResourceGoal.hpp | 19 +++- src/creature/creature.cpp | 38 +++++-- src/creature/goal.cpp | 169 +++++++++++++++++++++------- src/creature/need.cpp | 27 +++-- src/graphics/shader.cpp | 2 +- src/world/Planet.hpp | 17 ++- src/world/world.cpp | 65 +++++++---- tst/assert.hpp | 17 +++ tst/world/PlanetTest.cpp | 79 +++++++++++++ tst/world/PlanetTest.hpp | 32 ++++++ 15 files changed, 417 insertions(+), 102 deletions(-) create mode 100644 tst/world/PlanetTest.cpp create mode 100644 tst/world/PlanetTest.hpp diff --git a/src/app/app.cpp b/src/app/app.cpp index b4ede47..e4dc1a5 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -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() { diff --git a/src/blobs.cpp b/src/blobs.cpp index 18e62aa..006644b 100644 --- a/src/blobs.cpp +++ b/src/blobs.cpp @@ -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)) ; diff --git a/src/creature/Creature.hpp b/src/creature/Creature.hpp index aaeb6cf..ce56dfb 100644 --- a/src/creature/Creature.hpp +++ b/src/creature/Creature.hpp @@ -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 ∼ std::string name; + double size; double health; std::vector> needs; diff --git a/src/creature/Goal.hpp b/src/creature/Goal.hpp index d664e6b..2ec03bf 100644 --- a/src/creature/Goal.hpp +++ b/src/creature/Goal.hpp @@ -1,18 +1,34 @@ #ifndef BLOBS_CREATURE_GOAL_HPP_ #define BLOBS_CREATURE_GOAL_HPP_ +#include +#include + + namespace blobs { namespace creature { class Creature; +class Situation; +class Steering; class Goal { public: - Goal(); + using Callback = std::function; + +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; diff --git a/src/creature/IngestNeed.hpp b/src/creature/IngestNeed.hpp index 4244293..b0262b8 100644 --- a/src/creature/IngestNeed.hpp +++ b/src/creature/IngestNeed.hpp @@ -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; }; diff --git a/src/creature/LocateResourceGoal.hpp b/src/creature/LocateResourceGoal.hpp index b5d390c..afa5b93 100644 --- a/src/creature/LocateResourceGoal.hpp +++ b/src/creature/LocateResourceGoal.hpp @@ -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; }; diff --git a/src/creature/creature.cpp b/src/creature/creature.cpp index 1278e26..043aed7 100644 --- a/src/creature/creature.cpp +++ b/src/creature/creature.cpp @@ -16,13 +16,16 @@ #include #include +#include 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 &&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(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(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]); + } } } diff --git a/src/creature/goal.cpp b/src/creature/goal.cpp index 1b9d5d0..18714e4 100644 --- a/src/creature/goal.cpp +++ b/src/creature/goal.cpp @@ -3,13 +3,21 @@ #include "Creature.hpp" #include "../world/Planet.hpp" +#include "../world/Resource.hpp" +#include "../world/Simulation.hpp" #include "../world/TileType.hpp" +#include +#include + + 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 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; +} + } } diff --git a/src/creature/need.cpp b/src/creature/need.cpp index 60e7cb8..64ebf5b 100644 --- a/src/creature/need.cpp +++ b/src/creature/need.cpp @@ -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(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(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; + } } diff --git a/src/graphics/shader.cpp b/src/graphics/shader.cpp index c63a280..1b641b0 100644 --- a/src/graphics/shader.cpp +++ b/src/graphics/shader.cpp @@ -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" diff --git a/src/world/Planet.hpp b/src/world/Planet.hpp index 8cd9efe..2274d04 100644 --- a/src/world/Planet.hpp +++ b/src/world/Planet.hpp @@ -9,6 +9,7 @@ #include "../graphics/SimpleVAO.hpp" #include +#include #include #include @@ -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 &); void Draw(app::Assets &, graphics::Viewport &) override; @@ -77,7 +86,7 @@ private: glm::vec3 position; glm::vec3 tex_coord; }; - graphics::SimpleVAO vao; + std::unique_ptr> vao; }; diff --git a/src/world/world.cpp b/src/world/world.cpp index 0f9cb1e..0b1260f 100644 --- a/src/world/world.cpp +++ b/src/world/world.cpp @@ -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 &ts) { - vao.Bind(); - vao.BindAttributes(); - vao.EnableAttribute(0); - vao.EnableAttribute(1); - vao.AttributePointer(0, false, offsetof(Attributes, position)); - vao.AttributePointer(1, false, offsetof(Attributes, tex_coord)); - vao.ReserveAttributes(TilesTotal() * 4, GL_STATIC_DRAW); + vao.reset(new graphics::SimpleVAO); + vao->Bind(); + vao->BindAttributes(); + vao->EnableAttribute(0); + vao->EnableAttribute(1); + vao->AttributePointer(0, false, offsetof(Attributes, position)); + vao->AttributePointer(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 &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 &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); } diff --git a/tst/assert.hpp b/tst/assert.hpp index 85e4324..5042180 100644 --- a/tst/assert.hpp +++ b/tst/assert.hpp @@ -10,6 +10,23 @@ namespace blobs { namespace test { +template +void AssertEqual( + const std::string &msg, + const glm::tvec2 &expected, + const glm::tvec2 &actual, + T epsilon = std::numeric_limits::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 void AssertEqual( const std::string &msg, diff --git a/tst/world/PlanetTest.cpp b/tst/world/PlanetTest.cpp new file mode 100644 index 0000000..1ab0bf4 --- /dev/null +++ b/tst/world/PlanetTest.cpp @@ -0,0 +1,79 @@ +#include "PlanetTest.hpp" + +#include "../assert.hpp" + +#include "world/Planet.hpp" + +#include + +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::epsilon()); + + CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( + "wrong scalar conversion", + -2.5, p.TileToPosition(0), std::numeric_limits::epsilon()); + CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( + "wrong scalar conversion", + -0.5, p.TileToPosition(2), std::numeric_limits::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 index 0000000..1fc4b15 --- /dev/null +++ b/tst/world/PlanetTest.hpp @@ -0,0 +1,32 @@ +#ifndef BLOBS_TEST_WORLD_PLANETTEST_HPP_ +#define BLOBS_TEST_WORLD_PLANETTEST_HPP_ + +#include + + +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 -- 2.39.2