From: Daniel Karbach Date: Wed, 6 Dec 2017 21:27:58 +0000 (+0100) Subject: spherical planets X-Git-Url: http://git.localhorst.tv/?p=blobs.git;a=commitdiff_plain;h=cd80d7cfcac3c58d601db2ab4e0381dd77c06f44 spherical planets --- diff --git a/src/app/app.cpp b/src/app/app.cpp index 3f87c8f..913f9f2 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -176,7 +176,7 @@ Assets::Assets() , 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), diff --git a/src/app/states.cpp b/src/app/states.cpp index 4951c9f..92879b4 100644 --- a/src/app/states.cpp +++ b/src/app/states.cpp @@ -109,7 +109,7 @@ void MasterState::OnMouseMotion(const SDL_MouseMotionEvent &e) { 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); } } diff --git a/src/blobs.cpp b/src/blobs.cpp index 01bfb2a..1d854fc 100644 --- a/src/blobs.cpp +++ b/src/blobs.cpp @@ -13,7 +13,6 @@ #include #include - using namespace blobs; namespace { @@ -98,22 +97,6 @@ int main(int argc, char *argv[]) { 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); diff --git a/src/creature/LocateResourceGoal.hpp b/src/creature/LocateResourceGoal.hpp index f0937c9..c87f4f1 100644 --- a/src/creature/LocateResourceGoal.hpp +++ b/src/creature/LocateResourceGoal.hpp @@ -26,14 +26,12 @@ public: 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; diff --git a/src/creature/Memory.hpp b/src/creature/Memory.hpp index 3bc3908..c58fd26 100644 --- a/src/creature/Memory.hpp +++ b/src/creature/Memory.hpp @@ -19,8 +19,7 @@ class Memory { public: struct Location { world::Planet *planet; - int surface; - glm::ivec2 coords; + glm::dvec3 position; }; public: diff --git a/src/creature/Situation.hpp b/src/creature/Situation.hpp index 4d635f5..25d218d 100644 --- a/src/creature/Situation.hpp +++ b/src/creature/Situation.hpp @@ -53,10 +53,7 @@ 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; @@ -72,12 +69,11 @@ public: 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, diff --git a/src/creature/creature.cpp b/src/creature/creature.cpp index d7a9724..a9c4dc7 100644 --- a/src/creature/creature.cpp +++ b/src/creature/creature.cpp @@ -399,7 +399,7 @@ void Creature::TickState(double dt) { 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))); } @@ -415,8 +415,8 @@ Situation::Derivative Creature::Step(const Situation::Derivative &ds, double dt) 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() @@ -491,10 +491,16 @@ math::AABB Creature::CollisionBox() const noexcept { 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 { @@ -599,8 +605,8 @@ void Creature::Draw(graphics::Viewport &viewport) { 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; @@ -718,7 +724,7 @@ void Split(Creature &c) { 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; @@ -732,7 +738,7 @@ void Split(Creature &c) { } 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; @@ -754,13 +760,13 @@ void Memory::Erase() { 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) { @@ -808,7 +814,6 @@ std::string NameGenerator::Sequential() { Situation::Situation() : planet(nullptr) , state(glm::dvec3(0.0), glm::dvec3(0.0)) -, surface(0) , type(LOST) { } @@ -823,25 +828,12 @@ bool Situation::OnSurface() const noexcept { 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 { @@ -856,24 +848,16 @@ void Situation::Accelerate(const glm::dvec3 &dv) 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); } diff --git a/src/creature/goal.cpp b/src/creature/goal.cpp index 3f61e2e..4124876 100644 --- a/src/creature/goal.cpp +++ b/src/creature/goal.cpp @@ -219,7 +219,7 @@ void IngestGoal::Action() { } bool IngestGoal::OnSuitableTile() { - if (!GetSituation().OnTile()) { + if (!GetSituation().OnSurface()) { return false; } const world::TileType &t = GetSituation().GetTileType(); @@ -315,8 +315,6 @@ LocateResourceGoal::LocateResourceGoal(Creature &c) , accept() , found(false) , target_pos(0.0) -, target_srf(0) -, target_tile(0) , searching(false) , reevaluate(0.0) { } @@ -355,7 +353,7 @@ void LocateResourceGoal::Action() { GetSteering().GoTo(target_pos); } } - } else if (OnTargetTile()) { + } else if (NearTarget()) { GetSteering().Halt(); if (!GetSituation().Moving()) { SetComplete(); @@ -367,7 +365,7 @@ void LocateResourceGoal::Action() { } 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()) { @@ -376,8 +374,6 @@ void LocateResourceGoal::LocateResource() { 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(); @@ -391,51 +387,52 @@ void LocateResourceGoal::LocateResource() { 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]; } } } @@ -443,29 +440,24 @@ void LocateResourceGoal::SearchVicinity() { 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; } } diff --git a/src/graphics/Camera.hpp b/src/graphics/Camera.hpp index 1632916..e4c0b7f 100644 --- a/src/graphics/Camera.hpp +++ b/src/graphics/Camera.hpp @@ -34,15 +34,8 @@ public: 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); diff --git a/src/graphics/PlanetSurface.hpp b/src/graphics/PlanetSurface.hpp index b915283..27bd2bc 100644 --- a/src/graphics/PlanetSurface.hpp +++ b/src/graphics/PlanetSurface.hpp @@ -31,7 +31,6 @@ public: 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; @@ -57,7 +56,6 @@ private: GLuint mv_handle; GLuint mvp_handle; GLuint sampler_handle; - GLuint normal_handle; GLuint num_lights_handle; GLuint light_handle[MAX_LIGHTS * 3]; diff --git a/src/graphics/shader.cpp b/src/graphics/shader.cpp index 0890dfd..b896cfd 100644 --- a/src/graphics/shader.cpp +++ b/src/graphics/shader.cpp @@ -190,18 +190,21 @@ PlanetSurface::PlanetSurface() "#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" ); @@ -216,16 +219,17 @@ PlanetSurface::PlanetSurface() "};\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" @@ -253,7 +257,6 @@ PlanetSurface::PlanetSurface() 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"); @@ -310,10 +313,6 @@ void PlanetSurface::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm:: 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(); diff --git a/src/graphics/viewport.cpp b/src/graphics/viewport.cpp index 286fe51..1e7ea4b 100644 --- a/src/graphics/viewport.cpp +++ b/src/graphics/viewport.cpp @@ -60,66 +60,12 @@ Camera &Camera::Reference(const world::Body &r) noexcept { 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()); @@ -128,10 +74,10 @@ Camera &Camera::Radial(const creature::Creature &c, double distance, const glm:: 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 { diff --git a/src/world/Planet.hpp b/src/world/Planet.hpp index 7ac6b57..2371599 100644 --- a/src/world/Planet.hpp +++ b/src/world/Planet.hpp @@ -35,16 +35,33 @@ public: 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 &); + 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); @@ -52,10 +69,6 @@ public: 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(); @@ -65,38 +78,13 @@ public: 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 &); - void Draw(app::Assets &, graphics::Viewport &) override; - private: int sidelength; std::vector tiles; struct Attributes { glm::vec3 position; + glm::vec3 normal; glm::vec3 tex_coord; }; std::unique_ptr> vao; diff --git a/src/world/world.cpp b/src/world/world.cpp index b80be09..49380ac 100644 --- a/src/world/world.cpp +++ b/src/world/world.cpp @@ -355,28 +355,107 @@ Planet::Planet(int sidelength) 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 &ts) { @@ -385,8 +464,10 @@ void Planet::BuildVAO(const Set &ts) { vao->BindAttributes(); vao->EnableAttribute(0); vao->EnableAttribute(1); + vao->EnableAttribute(2); vao->AttributePointer(0, false, offsetof(Attributes, position)); - vao->AttributePointer(1, false, offsetof(Attributes, tex_coord)); + vao->AttributePointer(1, false, offsetof(Attributes, normal)); + vao->AttributePointer(2, false, offsetof(Attributes, tex_coord)); vao->ReserveAttributes(TilesTotal() * 4, GL_STATIC_DRAW); { auto attrib = vao->MapAttributes(GL_WRITE_ONLY); @@ -398,33 +479,44 @@ void Planet::BuildVAO(const Set &ts) { 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; @@ -469,19 +561,7 @@ void Planet::Draw(app::Assets &assets, graphics::Viewport &viewport) { 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); } diff --git a/tst/world/PlanetTest.cpp b/tst/world/PlanetTest.cpp index 1ab0bf4..3431801 100644 --- a/tst/world/PlanetTest.cpp +++ b/tst/world/PlanetTest.cpp @@ -31,47 +31,6 @@ void PlanetTest::testPositionConversion() { 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) - ); } }