, font_path(path + "fonts/")
, skin_path(path + "skins/")
, tile_path(path + "tiles/")
-, random(0x6283B64CEFE47925)
+, random(0x6283B64CEFE57925)
, fonts{
graphics::Font(font_path + "DejaVuSans.ttf", 32),
graphics::Font(font_path + "DejaVuSans.ttf", 24),
constexpr double pitch_scale = PI * 0.001;
constexpr double yaw_scale = PI * 0.002;
if (cam_dragging) {
- cam_orient.x = glm::clamp(cam_orient.x + double(e.yrel) * pitch_scale, 0.0, PI * 0.5);
+ cam_orient.x = glm::clamp(cam_orient.x + double(e.yrel) * pitch_scale, 0.0, PI * 0.499);
cam_orient.y = std::fmod(cam_orient.y + double(e.xrel) * yaw_scale, PI * 2.0);
}
}
#include <cstdint>
#include <iostream>
-
using namespace blobs;
namespace {
blob->BuildVAO();
app::MasterState state(assets, sim);
- state.GetCamera()
- .Reference(planet)
- // sunrise
- //.FirstPerson(0, glm::vec3(0.0f, 0.0f, 0.1f), glm::vec3(1.0f, -0.75f, 0.1f))
- // 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)
- // from afar, rotating
- //.Orbital(glm::vec3(-60.0f, 0.0f, 0.0f))
- ;
- // system view
- //state.GetCamera()
- // .Reference(sun)
- // .Orbital(glm::vec3(-500.0f, 500.0f, 500.0f))
- //;
state.GetCreaturePanel().Show(*blob);
state.GetTimePanel().SetBody(planet);
private:
void LocateResource();
void SearchVicinity();
- bool OnTargetTile() const noexcept;
+ bool NearTarget() const noexcept;
private:
Composition accept;
bool found;
glm::dvec3 target_pos;
- int target_srf;
- glm::ivec2 target_tile;
bool searching;
double reevaluate;
public:
struct Location {
world::Planet *planet;
- int surface;
- glm::ivec2 coords;
+ glm::dvec3 position;
};
public:
bool OnPlanet() const noexcept;
world::Planet &GetPlanet() const noexcept { return *planet; }
bool OnSurface() const noexcept;
- int Surface() const noexcept { return surface; }
const glm::dvec3 &Position() const noexcept { return state.pos; }
- bool OnTile() const noexcept;
- glm::ivec2 SurfacePosition() const noexcept;
world::Tile &GetTile() const noexcept;
const world::TileType &GetTileType() const noexcept;
void Heading(const glm::dvec3 &h) noexcept { state.dir = h; }
const glm::dvec3 &Heading() const noexcept { return state.dir; }
- void SetPlanetSurface(world::Planet &, int srf, const glm::dvec3 &pos) noexcept;
+ void SetPlanetSurface(world::Planet &, const glm::dvec3 &pos) noexcept;
public:
world::Planet *planet;
State state;
- int surface;
enum {
LOST,
PLANET_SURFACE,
if (ang < turn_rate) {
state.dir = normalize(state.vel);
} else if (std::abs(ang - PI) < 0.001) {
- state.dir = rotate(state.dir, turn_rate, world::Planet::SurfaceNormal(situation.Surface()));
+ state.dir = rotate(state.dir, turn_rate, situation.GetPlanet().NormalAt(state.pos));
} else {
state.dir = rotate(state.dir, turn_rate, normalize(cross(state.dir, nvel)));
}
s.vel += ds.acc * dt;
glm::dvec3 force(steering.Force(s));
// gravity = antinormal * mass * Gm / r²
- double elevation = s.pos[(situation.Surface() + 2) % 3];
- glm::dvec3 normal(world::Planet::SurfaceNormal(situation.Surface()));
+ double elevation = situation.GetPlanet().DistanceAt(s.pos);
+ glm::dvec3 normal(situation.GetPlanet().NormalAt(s.pos));
force += glm::dvec3(
-normal
* Mass() * situation.GetPlanet().GravitationalParameter()
glm::dmat4 Creature::CollisionTransform() const noexcept {
const double half_size = size * 0.5;
const glm::dvec3 &pos = situation.Position();
- const glm::dmat3 srf(world::Planet::SurfaceOrientation(situation.Surface()));
+ glm::dmat3 orient;
+ orient[1] = situation.GetPlanet().NormalAt(pos);
+ orient[2] = situation.Heading();
+ if (std::abs(dot(orient[1], orient[2])) > 0.999) {
+ orient[2] = glm::dvec3(orient[1].z, orient[1].x, orient[1].y);
+ }
+ orient[0] = normalize(cross(orient[1], orient[2]));
+ orient[2] = normalize(cross(orient[0], orient[1]));
return glm::translate(glm::dvec3(pos.x, pos.y, pos.z + half_size))
- * glm::rotate(glm::orientedAngle(-srf[2], situation.Heading(), srf[1]), srf[1])
- * glm::dmat4(srf);
+ * glm::dmat4(orient);
}
glm::dmat4 Creature::LocalTransform() noexcept {
void Spawn(Creature &c, world::Planet &p) {
p.AddCreature(&c);
- c.GetSituation().SetPlanetSurface(p, 0, p.TileCenter(0, p.SideLength() / 2, p.SideLength() / 2));
- c.GetSituation().Heading(-world::Planet::SurfaceOrientation(0)[2]);
+ c.GetSituation().SetPlanetSurface(p, glm::dvec3(0.0, 0.0, p.Radius()));
+ c.GetSituation().Heading(glm::dvec3(1.0, 0.0, 0.0));
// probe surrounding area for common resources
int start = p.SideLength() / 2 - 2;
s.GetPlanet().AddCreature(a);
// TODO: duplicate situation somehow
a->GetSituation().SetPlanetSurface(
- s.GetPlanet(), s.Surface(),
+ s.GetPlanet(),
s.Position() + glm::dvec3(0.0, 0.55 * a->Size(), 0.0));
a->BuildVAO();
c.GetSimulation().Log() << a->Name() << " was born" << std::endl;
}
s.GetPlanet().AddCreature(b);
b->GetSituation().SetPlanetSurface(
- s.GetPlanet(), s.Surface(),
+ s.GetPlanet(),
s.Position() - glm::dvec3(0.0, 0.55 * b->Size(), 0.0));
b->BuildVAO();
c.GetSimulation().Log() << b->Name() << " was born" << std::endl;
void Memory::Tick(double dt) {
Situation &s = c.GetSituation();
- if (s.OnTile()) {
- TrackStay({ &s.GetPlanet(), s.Surface(), s.SurfacePosition() }, dt);
+ if (s.OnSurface()) {
+ TrackStay({ &s.GetPlanet(), s.Position() }, dt);
}
}
void Memory::TrackStay(const Location &l, double t) {
- const world::TileType &type = l.planet->TypeAt(l.surface, l.coords.x, l.coords.y);
+ const world::TileType &type = l.planet->TileTypeAt(l.position);
auto entry = known_types.find(type.id);
if (entry != known_types.end()) {
if (c.GetSimulation().Time() - entry->second.last_been > c.GetProperties().Lifetime() * 0.1) {
Situation::Situation()
: planet(nullptr)
, state(glm::dvec3(0.0), glm::dvec3(0.0))
-, surface(0)
, type(LOST) {
}
return type == PLANET_SURFACE;
}
-bool Situation::OnTile() const noexcept {
- if (type != PLANET_SURFACE) return false;
- glm::ivec2 t(planet->SurfacePosition(surface, state.pos));
- return t.x >= 0 && t.x < planet->SideLength()
- && t.y >= 0 && t.y < planet->SideLength();
-}
-
-glm::ivec2 Situation::SurfacePosition() const noexcept {
- return planet->SurfacePosition(surface, state.pos);
-}
-
world::Tile &Situation::GetTile() const noexcept {
- glm::ivec2 t(planet->SurfacePosition(surface, state.pos));
- return planet->TileAt(surface, t.x, t.y);
+ return planet->TileAt(state.pos);
}
const world::TileType &Situation::GetTileType() const noexcept {
- glm::ivec2 t(planet->SurfacePosition(surface, state.pos));
- return planet->TypeAt(surface, t.x, t.y);
+ return planet->TileTypeAt(state.pos);
}
void Situation::Move(const glm::dvec3 &dp) noexcept {
void Situation::EnforceConstraints(State &s) noexcept {
if (OnSurface()) {
- if (Surface() < 3) {
- if (s.pos[(Surface() + 2) % 3] < GetPlanet().Radius()) {
- s.pos[(Surface() + 2) % 3] = GetPlanet().Radius();
- s.vel[(Surface() + 2) % 3] = std::max(0.0, s.vel[(Surface() + 2) % 3]);
- }
- } else {
- if (s.pos[(Surface() + 2) % 3] > -GetPlanet().Radius()) {
- s.pos[(Surface() + 2) % 3] = -GetPlanet().Radius();
- s.vel[(Surface() + 2) % 3] = std::min(0.0, s.vel[(Surface() + 2) % 3]);
- }
+ double r = GetPlanet().Radius();
+ if (length2(s.pos) < r * r) {
+ s.pos = normalize(s.pos) * r;
}
}
}
-void Situation::SetPlanetSurface(world::Planet &p, int srf, const glm::dvec3 &pos) noexcept {
+void Situation::SetPlanetSurface(world::Planet &p, const glm::dvec3 &pos) noexcept {
type = PLANET_SURFACE;
planet = &p;
- surface = srf;
state.pos = pos;
EnforceConstraints(state);
}
}
bool IngestGoal::OnSuitableTile() {
- if (!GetSituation().OnTile()) {
+ if (!GetSituation().OnSurface()) {
return false;
}
const world::TileType &t = GetSituation().GetTileType();
, accept()
, found(false)
, target_pos(0.0)
-, target_srf(0)
-, target_tile(0)
, searching(false)
, reevaluate(0.0) {
}
GetSteering().GoTo(target_pos);
}
}
- } else if (OnTargetTile()) {
+ } else if (NearTarget()) {
GetSteering().Halt();
if (!GetSituation().Moving()) {
SetComplete();
}
void LocateResourceGoal::LocateResource() {
- if (GetSituation().OnTile()) {
+ if (GetSituation().OnSurface()) {
const world::TileType &t = GetSituation().GetTileType();
auto yield = t.FindBestResource(accept);
if (yield != t.resources.cend()) {
found = true;
searching = false;
target_pos = GetSituation().Position();
- target_srf = GetSituation().Surface();
- target_tile = GetSituation().GetPlanet().SurfacePosition(target_srf, target_pos);
} else {
// go find somewhere else
SearchVicinity();
void LocateResourceGoal::SearchVicinity() {
const world::Planet &planet = GetSituation().GetPlanet();
- int srf = GetSituation().Surface();
const glm::dvec3 &pos = GetSituation().Position();
+ const glm::dvec3 normal(planet.NormalAt(pos));
+ const glm::dvec3 step_x(normalize(cross(normal, glm::dvec3(normal.z, normal.x, normal.y))));
+ const glm::dvec3 step_y(normalize(cross(step_x, normal)));
- 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 + glm::ivec2(1)));
-
- double rating[end.y - begin.y][end.x - begin.x];
- std::memset(rating, 0, sizeof(double) * (end.y - begin.y) * (end.x - begin.x));
+ constexpr int search_radius = 2;
+ double rating[2 * search_radius + 1][2 * search_radius + 1] = {0};
// find close and rich field
- 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);
+ for (int y = -search_radius; y < search_radius + 1; ++y) {
+ for (int x = -search_radius; x < search_radius + 1; ++x) {
+ const glm::dvec3 tpos(pos + (double(x) * step_x) + (double(y) * step_y));
+ if (!GetCreature().PerceptionTest(tpos)) continue;
+ const world::TileType &type = planet.TileTypeAt(tpos);
auto yield = type.FindBestResource(accept);
if (yield != type.resources.cend()) {
- glm::dvec3 tc(planet.TileCenter(srf, x, y));
- if (!GetCreature().PerceptionTest(tc)) continue;
// TODO: subtract minimum yield
- rating[y - begin.y][x - begin.x] = yield->ubiquity * accept.Get(yield->resource);
- double dist = std::max(0.125, 0.25 * glm::length(tc - pos));
- rating[y - begin.y][x - begin.x] /= dist;
+ rating[y + search_radius][x + search_radius] = yield->ubiquity * accept.Get(yield->resource);
+ // penalize distance
+ double dist = std::max(0.125, 0.25 * glm::length(tpos - pos));
+ rating[y + search_radius][x + search_radius] /= dist;
}
}
}
- // demote crowded tiles
+ // penalize crowding
for (auto &c : planet.Creatures()) {
if (&*c == &GetCreature()) continue;
- if (c->GetSituation().Surface() != srf) continue;
- glm::ivec2 coords(c->GetSituation().SurfacePosition());
- if (coords.x < begin.x || coords.x >= end.x) continue;
- if (coords.y < begin.y || coords.y >= end.y) continue;
- rating[coords.y - begin.y][coords.x - begin.x] *= 0.8;
+ for (int y = -search_radius; y < search_radius + 1; ++y) {
+ for (int x = -search_radius; x < search_radius + 1; ++x) {
+ const glm::dvec3 tpos(pos + (double(x) * step_x) + (double(y) * step_y));
+ if (length2(tpos - c->GetSituation().Position()) < 1.0) {
+ rating[y + search_radius][x + search_radius] *= 0.8;
+ }
+ }
+ }
}
glm::ivec2 best_pos(0);
double best_rating = -1.0;
- for (int y = begin.y; y < end.y; ++y) {
- for (int x = begin.x; x < end.x; ++x) {
- if (rating[y - begin.y][x - begin.x] > best_rating) {
+ for (int y = -search_radius; y < search_radius + 1; ++y) {
+ for (int x = -search_radius; x < search_radius + 1; ++x) {
+ if (rating[y + search_radius][x + search_radius] > best_rating) {
best_pos = glm::ivec2(x, y);
- best_rating = rating[y - begin.y][x - begin.x];
+ best_rating = rating[y + search_radius][x + search_radius];
}
}
}
if (best_rating) {
found = true;
searching = false;
- target_pos = planet.TileCenter(srf, best_pos.x, best_pos.y);
- target_srf = srf;
- target_tile = best_pos;
+ target_pos = normalize(pos + (double(best_pos.x) * step_x) + (double(best_pos.y) * step_y)) * planet.Radius();
GetSteering().GoTo(target_pos);
} else if (!searching) {
found = false;
searching = true;
target_pos = GetSituation().Position();
- target_pos[(srf + 0) % 3] += Assets().random.SNorm();
- target_pos[(srf + 1) % 3] += Assets().random.SNorm();
+ target_pos += Assets().random.SNorm() * step_x;
+ target_pos += Assets().random.SNorm() * step_y;
// bias towards current heading
target_pos += GetSituation().Heading() * 0.5;
- target_pos = clamp(target_pos, -planet.Radius(), planet.Radius());
+ target_pos = normalize(target_pos) * planet.Radius();
GetSteering().GoTo(target_pos);
}
}
-bool LocateResourceGoal::OnTargetTile() const noexcept {
+bool LocateResourceGoal::NearTarget() const noexcept {
const Situation &s = GetSituation();
- return s.OnSurface()
- && s.Surface() == target_srf
- && s.OnTile()
- && s.SurfacePosition() == target_tile;
+ return s.OnSurface() && length2(s.Position() - target_pos) < 0.5;
}
}
const world::Body &Reference() const noexcept { return *ref; }
Camera &Reference(const world::Body &) noexcept;
- /// standing on given surface, with pos.z being elevation over NN
- /// looking at given coordinates
- Camera &FirstPerson(int surface, const glm::vec3 &pos, const glm::vec3 &at) noexcept;
- /// looking straight down at surface from above
- Camera &MapView(int surface, const glm::vec3 &pos, float roll = 0.0f) noexcept;
/// look at center, position relative to orbital reference plane for children
Camera &Orbital(const glm::vec3 &pos) noexcept;
- /// look at creature from above
- Camera &TopDown(const creature::Creature &, double distance, double roll = 0.0f);
/// look at creature from the side, angle in euler (ZXY in surface reference plane)
Camera &Radial(const creature::Creature &, double distance, const glm::dvec3 &angle);
void SetV(const glm::mat4 &v) noexcept;
void SetVP(const glm::mat4 &v, const glm::mat4 &p) noexcept;
void SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept;
- void SetNormal(const glm::vec3 &) noexcept;
void SetTexture(ArrayTexture &) noexcept;
void SetLight(int n, const glm::vec3 &pos, const glm::vec3 &color, float strength) noexcept;
void SetNumLights(int n) noexcept;
GLuint mv_handle;
GLuint mvp_handle;
GLuint sampler_handle;
- GLuint normal_handle;
GLuint num_lights_handle;
GLuint light_handle[MAX_LIGHTS * 3];
"#version 330 core\n"
"layout(location = 0) in vec3 vtx_position;\n"
- "layout(location = 1) in vec3 vtx_tex_uv;\n"
+ "layout(location = 1) in vec3 vtx_normal;\n"
+ "layout(location = 2) in vec3 vtx_tex_uv;\n"
"uniform mat4 M;\n"
"uniform mat4 MV;\n"
"uniform mat4 MVP;\n"
- "out vec3 frag_tex_uv;\n"
"out vec3 vtx_viewspace;\n"
+ "out vec3 nrm_viewspace;\n"
+ "out vec3 frag_tex_uv;\n"
"void main() {\n"
- "gl_Position = MVP * vec4(vtx_position, 1);\n"
- "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
+ "gl_Position = MVP * vec4(vtx_position, 1.0);\n"
+ "vtx_viewspace = (MV * vec4(vtx_position, 1.0)).xyz;\n"
+ "nrm_viewspace = (MV * vec4(vtx_position, 0.0)).xyz;\n"
"frag_tex_uv = vtx_tex_uv;\n"
"}\n"
);
"};\n"
"in vec3 vtx_viewspace;\n"
+ "in vec3 nrm_viewspace;\n"
"in vec3 frag_tex_uv;\n"
"uniform sampler2DArray tex_sampler;\n"
- "uniform vec3 normal;\n"
"uniform int num_lights;\n"
"uniform LightSource light[8];\n"
"out vec3 color;\n"
"void main() {\n"
+ "vec3 normal = normalize(nrm_viewspace);\n"
"vec3 tex_color = texture(tex_sampler, frag_tex_uv).rgb;\n"
"vec3 total_light = tex_color * vec3(0.1, 0.1, 0.1);\n"
"for (int i = 0; i < num_lights; ++i) {\n"
mv_handle = prog.UniformLocation("MV");
mvp_handle = prog.UniformLocation("MVP");
sampler_handle = prog.UniformLocation("tex_sampler");
- normal_handle = prog.UniformLocation("normal");
num_lights_handle = prog.UniformLocation("num_lights");
for (int i = 0; i < MAX_LIGHTS; ++i) {
light_handle[3 * i + 0] = prog.UniformLocation("light[" + std::to_string(i) + "].position");
prog.Uniform(mvp_handle, mvp);
}
-void PlanetSurface::SetNormal(const glm::vec3 &n) noexcept {
- prog.Uniform(normal_handle, n);
-}
-
void PlanetSurface::SetTexture(ArrayTexture &tex) noexcept {
glActiveTexture(GL_TEXTURE0);
tex.Bind();
return *this;
}
-Camera &Camera::FirstPerson(int srf, const glm::vec3 &pos, const glm::vec3 &at) noexcept {
- track_orient = true;
-
- float dir = srf < 3 ? 1.0f : -1.0f;
-
- glm::vec3 position;
- position[(srf + 0) % 3] = pos.x;
- position[(srf + 1) % 3] = pos.y;
- position[(srf + 2) % 3] = dir * (pos.z + Reference().Radius());
-
- glm::vec3 up(world::Planet::SurfaceNormal(srf));
-
- glm::vec3 target;
- target[(srf + 0) % 3] = at.x;
- target[(srf + 1) % 3] = at.y;
- target[(srf + 2) % 3] = dir * (at.z + Reference().Radius());
-
- view = glm::lookAt(position, target, up);
-
- return *this;
-}
-
-Camera &Camera::MapView(int srf, const glm::vec3 &pos, float roll) noexcept {
- track_orient = true;
-
- float dir = srf < 3 ? 1.0f : -1.0f;
-
- glm::vec3 up(0.0f);
- up[(srf + 0) % 3] = std::sin(roll);
- up[(srf + 1) % 3] = std::cos(roll);
- up[(srf + 2) % 3] = 0.0f;
-
- glm::vec3 target = pos;
- target[(srf + 2) % 3] -= dir;
-
- view = glm::lookAt(pos, target, up);
-
- return *this;
-}
-
Camera &Camera::Orbital(const glm::vec3 &pos) noexcept {
track_orient = false;
view = glm::lookAt(pos, glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
return *this;
}
-Camera &Camera::TopDown(const creature::Creature &c, double distance, double roll) {
- const creature::Situation &s = c.GetSituation();
- if (s.OnSurface()) {
- int srf = s.Surface();
- glm::vec3 pos(s.Position() + (world::Planet::SurfaceNormal(srf) * distance));
- Reference(s.GetPlanet());
- return MapView(srf, pos, roll);
- } else {
- glm::vec3 pos(s.Position());
- pos += glm::normalize(pos) * float(distance);
- return Orbital(pos);
- }
-}
-
Camera &Camera::Radial(const creature::Creature &c, double distance, const glm::dvec3 &angle) {
const creature::Situation &s = c.GetSituation();
glm::dvec3 pos(s.Position());
if (s.OnSurface()) {
Reference(s.GetPlanet());
track_orient = true;
- int srf = s.Surface();
- up = world::Planet::SurfaceNormal(srf);
+ up = s.GetPlanet().NormalAt(s.Position());
+ glm::dvec3 ref(normalize(cross(up, glm::dvec3(up.z, up.x, up.y))));
dir =
- world::Planet::SurfaceOrientation(srf)
+ glm::dmat3(ref, up, cross(ref, up))
* glm::dmat3(glm::eulerAngleYX(-angle.y, -angle.x))
* dir;
} else {
Planet &operator =(Planet &&) = delete;
public:
+ /// surface normal
+ glm::dvec3 NormalAt(const glm::dvec3 &p) const noexcept { return normalize(p); }
+ /// height over surface
+ double ElevationAt(const glm::dvec3 &p) const noexcept { return length(p) - Radius(); }
+ /// distance to planet center
+ double DistanceAt(const glm::dvec3 &p) const noexcept { return length(p); }
+
+ /// get ground tile
+ Tile &TileAt(const glm::dvec3 &) noexcept;
+ const Tile &TileAt(const glm::dvec3 &) const noexcept;
+ const TileType &TileTypeAt(const glm::dvec3 &) const noexcept;
+
/// Get the tile at given surface and coordinates.
- Tile &TileAt(int surface, int x, int y) {
- return tiles[IndexOf(surface, x, y)];
- }
- const Tile &TileAt(int surface, int x, int y) const {
- return tiles[IndexOf(surface, x, y)];
- }
+ Tile &TileAt(int surface, int x, int y) noexcept;
+ const Tile &TileAt(int surface, int x, int y) const noexcept;
+ const TileType &TypeAt(int surface, int x, int y) const noexcept;
- const TileType &TypeAt(int surface, int x, int y) const;
+ /// The length in tiles of the side of each surface.
+ int SideLength() const { return sidelength; }
+
+ // 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;
+
+private:
/// Convert coordinates into a tile index.
int IndexOf(int surface, int x, int y) const {
assert(0 <= surface && surface <= 5);
assert(0 <= y && y < sidelength);
return surface * TilesPerSurface() + y * SideLength() + x;
}
- /// The length of the side of each surface.
- int SideLength() const {
- return sidelength;
- }
/// The number of tiles of one surface.
int TilesPerSurface() const {
return SideLength() * SideLength();
return 6 * TilesPerSurface();
}
- 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;
-
- static glm::dvec3 SurfaceNormal(int srf) noexcept {
- glm::dvec3 nrm(0.0);
- nrm[(srf + 2) % 3] = srf < 3 ? 1.0 : -1.0;
- return nrm;
- }
- static glm::dmat3 SurfaceOrientation(int srf) noexcept {
- glm::dmat3 mat(0.0);
- mat[(srf + 0) % 3][0] = 1.0;
- mat[(srf + 2) % 3][1] = srf < 3 ? -1.0 : 1.0;
- mat[(srf + 1) % 3][2] = srf < 3 ? 1.0 : -1.0;
- return mat;
- }
-
- void BuildVAO(const Set<TileType> &);
- void Draw(app::Assets &, graphics::Viewport &) override;
-
private:
int sidelength;
std::vector<Tile> tiles;
struct Attributes {
glm::vec3 position;
+ glm::vec3 normal;
glm::vec3 tex_coord;
};
std::unique_ptr<graphics::SimpleVAO<Attributes, unsigned int>> vao;
Planet::~Planet() {
}
-const TileType &Planet::TypeAt(int srf, int x, int y) const {
- return GetSimulation().TileTypes()[TileAt(srf, x, y).type];
+namespace {
+/// map p onto cube, s gives the surface, u and v the position in [-1,1]
+void cubemap(const glm::dvec3 &p, int &s, double &u, double &v) noexcept {
+ const glm::dvec3 p_abs(abs(p));
+ const glm::bvec3 p_pos(greaterThan(p, glm::dvec3(0.0)));
+ double max_axis = 0.0;
+
+ if (p_pos.x && p_abs.x >= p_abs.y && p_abs.x >= p_abs.z) {
+ max_axis = p_abs.x;
+ u = p.y;
+ v = p.z;
+ s = 1;
+ }
+ if (!p_pos.x && p_abs.x >= p_abs.y && p_abs.x >= p_abs.z) {
+ max_axis = p_abs.x;
+ u = -p.y;
+ v = -p.z;
+ s = 4;
+ }
+ if (p_pos.y && p_abs.y >= p_abs.x && p_abs.y >= p_abs.z) {
+ max_axis = p_abs.y;
+ u = p.z;
+ v = p.x;
+ s = 2;
+ }
+ if (!p_pos.y && p_abs.y >= p_abs.x && p_abs.y >= p_abs.z) {
+ max_axis = p_abs.y;
+ u = -p.z;
+ v = -p.x;
+ s = 5;
+ }
+ if (p_pos.z && p_abs.z >= p_abs.x && p_abs.z >= p_abs.y) {
+ max_axis = p_abs.z;
+ u = p.x;
+ v = p.y;
+ s = 0;
+ }
+ if (!p_pos.z && p_abs.z >= p_abs.x && p_abs.z >= p_abs.y) {
+ max_axis = p_abs.z;
+ u = -p.x;
+ v = -p.y;
+ s = 3;
+ }
+ u /= max_axis;
+ v /= max_axis;
+}
+/// get p from cube, s being surface, u and v the position in [-1,1],
+/// gives a vector from the center to the surface
+glm::dvec3 cubeunmap(int s, double u, double v) {
+ switch (s) {
+ default:
+ case 0: return glm::dvec3(u, v, 1.0); // +Z
+ case 1: return glm::dvec3(1.0, u, v); // +X
+ case 2: return glm::dvec3(v, 1.0, u); // +Y
+ case 3: return glm::dvec3(-u, -v, -1.0); // -Z
+ case 4: return glm::dvec3(-1.0, -u, -v); // -X
+ case 5: return glm::dvec3(-v, -1.0, -u); // -Y
+ };
+}
}
-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]));
+Tile &Planet::TileAt(const glm::dvec3 &p) noexcept {
+ int srf = 0;
+ double u = 0.0;
+ double v = 0.0;
+ cubemap(p, srf, u, v);
+ int x = glm::clamp(int(u * Radius() + Radius()), 0, sidelength - 1);
+ int y = glm::clamp(int(v * Radius() + Radius()), 0, sidelength - 1);
+ return TileAt(srf, x, y);
}
-double Planet::SurfaceElevation(int srf, const glm::dvec3 &pos) const noexcept {
- return srf < 3
- ? pos[(srf + 2) % 3] - Radius()
- : -pos[(srf + 2) % 3] - Radius();
+const Tile &Planet::TileAt(const glm::dvec3 &p) const noexcept {
+ int srf = 0;
+ double u = 0.0;
+ double v = 0.0;
+ cubemap(p, srf, u, v);
+ int x = glm::clamp(int(u * Radius() + Radius()), 0, sidelength - 1);
+ int y = glm::clamp(int(v * Radius() + Radius()), 0, sidelength - 1);
+ return TileAt(srf, x, y);
+}
+
+const TileType &Planet::TileTypeAt(const glm::dvec3 &p) const noexcept {
+ return GetSimulation().TileTypes()[TileAt(p).type];
+}
+
+Tile &Planet::TileAt(int surface, int x, int y) noexcept {
+ return tiles[IndexOf(surface, x, y)];
+}
+
+const Tile &Planet::TileAt(int surface, int x, int y) const noexcept {
+ return tiles[IndexOf(surface, x, y)];
+}
+
+const TileType &Planet::TypeAt(int srf, int x, int y) const noexcept {
+ return GetSimulation().TileTypes()[TileAt(srf, x, y).type];
}
glm::dvec3 Planet::TileCenter(int srf, int x, int y, double e) const noexcept {
- glm::dvec3 center(0.0f);
- 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;
+ double u = (double(x) - Radius() + 0.5) / Radius();
+ double v = (double(y) - Radius() + 0.5) / Radius();
+ return normalize(cubeunmap(srf, u, v)) * (Radius() + e);
}
void Planet::BuildVAO(const Set<TileType> &ts) {
vao->BindAttributes();
vao->EnableAttribute(0);
vao->EnableAttribute(1);
+ vao->EnableAttribute(2);
vao->AttributePointer<glm::vec3>(0, false, offsetof(Attributes, position));
- vao->AttributePointer<glm::vec3>(1, false, offsetof(Attributes, tex_coord));
+ vao->AttributePointer<glm::vec3>(1, false, offsetof(Attributes, normal));
+ vao->AttributePointer<glm::vec3>(2, false, offsetof(Attributes, tex_coord));
vao->ReserveAttributes(TilesTotal() * 4, GL_STATIC_DRAW);
{
auto attrib = vao->MapAttributes(GL_WRITE_ONLY);
for (int index = 0, surface = 0; surface < 6; ++surface) {
for (int y = 0; y < sidelength; ++y) {
for (int x = 0; x < sidelength; ++x, ++index) {
+ glm::vec3 pos[5];
+ pos[0][(surface + 0) % 3] = x + 0 - offset;
+ pos[0][(surface + 1) % 3] = y + 0 - offset;
+ pos[0][(surface + 2) % 3] = surface < 3 ? offset : -offset;
+ pos[1][(surface + 0) % 3] = x + 0 - offset;
+ pos[1][(surface + 1) % 3] = y + 1 - offset;
+ pos[1][(surface + 2) % 3] = surface < 3 ? offset : -offset;
+ pos[2][(surface + 0) % 3] = x + 1 - offset;
+ pos[2][(surface + 1) % 3] = y + 0 - offset;
+ pos[2][(surface + 2) % 3] = surface < 3 ? offset : -offset;
+ pos[3][(surface + 0) % 3] = x + 1 - offset;
+ pos[3][(surface + 1) % 3] = y + 1 - offset;
+ pos[3][(surface + 2) % 3] = surface < 3 ? offset : -offset;
+
float tex = ts[TileAt(surface, x, y).type].texture;
const float tex_v_begin = surface < 3 ? 1.0f : 0.0f;
const float tex_v_end = surface < 3 ? 0.0f : 1.0f;
- attrib[4 * index + 0].position[(surface + 0) % 3] = x + 0 - offset;
- attrib[4 * index + 0].position[(surface + 1) % 3] = y + 0 - offset;
- attrib[4 * index + 0].position[(surface + 2) % 3] = surface < 3 ? offset : -offset;
+
+ attrib[4 * index + 0].position = normalize(pos[0]) * offset;
+ attrib[4 * index + 0].normal = pos[0];
attrib[4 * index + 0].tex_coord[0] = 0.0f;
attrib[4 * index + 0].tex_coord[1] = tex_v_begin;
attrib[4 * index + 0].tex_coord[2] = tex;
- attrib[4 * index + 1].position[(surface + 0) % 3] = x + 0 - offset;
- attrib[4 * index + 1].position[(surface + 1) % 3] = y + 1 - offset;
- attrib[4 * index + 1].position[(surface + 2) % 3] = surface < 3 ? offset : -offset;
+ attrib[4 * index + 1].position = normalize(pos[1]) * offset;
+ attrib[4 * index + 1].normal = pos[1];
attrib[4 * index + 1].tex_coord[0] = 0.0f;
attrib[4 * index + 1].tex_coord[1] = tex_v_end;
attrib[4 * index + 1].tex_coord[2] = tex;
- attrib[4 * index + 2].position[(surface + 0) % 3] = x + 1 - offset;
- attrib[4 * index + 2].position[(surface + 1) % 3] = y + 0 - offset;
- attrib[4 * index + 2].position[(surface + 2) % 3] = surface < 3 ? offset : -offset;
+ attrib[4 * index + 2].position = normalize(pos[2]) * offset;
+ attrib[4 * index + 2].normal = pos[2];
attrib[4 * index + 2].tex_coord[0] = 1.0f;
attrib[4 * index + 2].tex_coord[1] = tex_v_begin;
attrib[4 * index + 2].tex_coord[2] = tex;
- attrib[4 * index + 3].position[(surface + 0) % 3] = x + 1 - offset;
- attrib[4 * index + 3].position[(surface + 1) % 3] = y + 1 - offset;
- attrib[4 * index + 3].position[(surface + 2) % 3] = surface < 3 ? offset : -offset;
+ attrib[4 * index + 3].position = normalize(pos[3]) * offset;
+ attrib[4 * index + 3].normal = pos[3];
attrib[4 * index + 3].tex_coord[0] = 1.0f;
attrib[4 * index + 3].tex_coord[1] = tex_v_end;
attrib[4 * index + 3].tex_coord[2] = tex;
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);
- 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);
- 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);
- 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);
- 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);
- 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(TilesTotal() * 6);
}
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)
- );
}
}