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);
LoadSkinTexture("7", textures.skins, 6);
LoadSkinTexture("8", textures.skins, 7);
LoadSkinTexture("9", textures.skins, 8);
+ textures.skins.FilterTrilinear();
}
Assets::~Assets() {
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");
// 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))
;
namespace world {
class Body;
class Planet;
+ class Simulation;
}
namespace creature {
class Creature {
public:
- Creature();
+ explicit Creature(world::Simulation &);
~Creature();
Creature(const Creature &) = delete;
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;
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;
void Draw(app::Assets &, graphics::Viewport &);
private:
+ world::Simulation ∼
std::string name;
+ double size;
double health;
std::vector<std::unique_ptr<Need>> needs;
#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; }
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;
namespace blobs {
namespace creature {
-class Need;
+class Goal;
class IngestNeed
: public Need {
void ApplyEffect(Creature &, double dt) override;
private:
+ void OnLocateComplete(Goal &);
+
+private:
+ Goal *locate_goal;
int resource;
double speed;
double damage;
bool ingesting;
- bool locating;
};
#ifndef BLOBS_CREATURE_LOCATERESOURCEGOAL_HPP_
#define BLOBS_CREATURE_LOCATERESOURCEGOAL_HPP_
+#include "../graphics/glm.hpp"
+
+
namespace blobs {
namespace creature {
: 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;
};
#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()
}
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));
}
}
// 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;
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() {
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;
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));
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));
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]);
+ }
}
}
#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) {
}
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;
+}
+
}
}
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() {
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;
+ }
}
"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"
#include "../graphics/SimpleVAO.hpp"
#include <cassert>
+#include <memory>
#include <vector>
#include <GL/glew.h>
/// 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.
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;
glm::vec3 position;
glm::vec3 tex_coord;
};
- graphics::SimpleVAO<Attributes, unsigned int> vao;
+ std::unique_ptr<graphics::SimpleVAO<Attributes, unsigned int>> vao;
};
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
}
}
}
- 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) {
}
}
}
- 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);
}
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,
--- /dev/null
+#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)
+ );
+}
+
+}
+}
+}
--- /dev/null
+#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