]> git.localhorst.tv Git - blank.git/commitdiff
unified location handling
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Tue, 3 Nov 2015 15:54:08 +0000 (16:54 +0100)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Tue, 3 Nov 2015 15:54:08 +0000 (16:54 +0100)
24 files changed:
src/ai/ai.cpp
src/client/net.cpp
src/geometry/Location.hpp [new file with mode: 0644]
src/io/WorldSave.cpp
src/io/WorldSave.hpp
src/net/net.cpp
src/server/net.cpp
src/shared/cli.cpp
src/world/Block.hpp
src/world/BlockLookup.hpp
src/world/Chunk.hpp
src/world/ChunkIndex.hpp
src/world/ChunkStore.hpp
src/world/Entity.hpp
src/world/EntityState.hpp
src/world/Generator.cpp
src/world/World.hpp
src/world/WorldCollision.hpp
src/world/chunk.cpp
src/world/world.cpp
tst/geometry/LocationTest.cpp [new file with mode: 0644]
tst/geometry/LocationTest.hpp [new file with mode: 0644]
tst/net/PacketTest.cpp
tst/world/ChunkTest.cpp

index 8386c59d174067b65fbcd241765902db023c09c2..a2f5938eb707c3c58fc9199c3759a935b51a3393 100644 (file)
@@ -84,7 +84,7 @@ void AIController::Update(Entity &e, float dt) {
                // our box is oriented for -Z velocity
                obstacle_transform = glm::mat4(find_rotation(glm::vec3(0.0f, 0.0f, -1.0f), e.Heading()));
                // and positioned relative to the entity's chunk
-               obstacle_transform[3] = glm::vec4(e.GetState().block_pos, 1.0f);
+               obstacle_transform[3] = glm::vec4(e.GetState().pos.block, 1.0f);
        }
 
        if (wandering) {
@@ -297,7 +297,7 @@ glm::vec3 AIController::GetObstacleAvoidanceForce(const Entity &e, const EntityS
        // point on the "velocity ray" closest to obstacle
        float to_go = dot(difference, e.Heading());
        // point is our future position if we keep going our way
-       glm::vec3 point(e.GetState().block_pos + e.Heading() * to_go);
+       glm::vec3 point(e.GetState().pos.block + e.Heading() * to_go);
        // now steer away in the direction of (point - block)
        // with a magniture proportional to speed/distance
        return normalize(point - nearest->BlockCoords()) * (e.Speed() / std::sqrt(distance));
@@ -429,7 +429,7 @@ glm::vec3 AIController::GetEvadeForce(const Entity &, const EntityState &state)
        glm::vec3 cur_diff(state.Diff(GetEvadeTarget().GetState()));
        float time_estimate = length(cur_diff) / evade_speed;
        EntityState pred_state(GetEvadeTarget().GetState());
-       pred_state.block_pos += pred_state.velocity * time_estimate;
+       pred_state.pos.block += pred_state.velocity * time_estimate;
        return Flee(state, pred_state, evade_speed, 2.0f);
 }
 
@@ -475,7 +475,7 @@ glm::vec3 AIController::GetPursuitForce(const Entity &, const EntityState &state
        glm::vec3 cur_diff(state.Diff(GetPursuitTarget().GetState()));
        float time_estimate = length(cur_diff) / pursuit_speed;
        EntityState pred_state(GetPursuitTarget().GetState());
-       pred_state.block_pos += pred_state.velocity * time_estimate;
+       pred_state.pos.block += pred_state.velocity * time_estimate;
        return Seek(state, pred_state, pursuit_speed, 2.0f);
 }
 
index 1c13f9e2912c7c79971a5042806079194bf0ab5e..d7a2661d56c3bc54d09df6140da9a003fed848db 100644 (file)
@@ -62,7 +62,7 @@ int ChunkReceiver::ToLoad() const noexcept {
 void ChunkReceiver::LoadOne() {
        if (!store.HasMissing()) return;
 
-       Chunk::Pos pos = store.NextMissing();
+       ExactLocation::Coarse pos = store.NextMissing();
        Chunk *chunk = store.Allocate(pos);
        if (!chunk) {
                // chunk store corrupted?
@@ -392,8 +392,7 @@ void NetworkedInput::MergePlayerCorrection(uint16_t seq, const EntityState &corr
        replay.SetState(corrected_state);
 
        if (entry != end) {
-               entry->state.chunk_pos = replay.GetState().chunk_pos;
-               entry->state.block_pos = replay.GetState().block_pos;
+               entry->state.pos = replay.GetState().pos;
                ++entry;
        }
 
@@ -401,8 +400,7 @@ void NetworkedInput::MergePlayerCorrection(uint16_t seq, const EntityState &corr
        while (entry != end) {
                SetMovement(entry->movement);
                GetWorld().Update(replay, entry->delta_t);
-               entry->state.chunk_pos = replay.GetState().chunk_pos;
-               entry->state.block_pos = replay.GetState().block_pos;
+               entry->state.pos = replay.GetState().pos;
                ++entry;
        }
 
@@ -421,13 +419,12 @@ void NetworkedInput::MergePlayerCorrection(uint16_t seq, const EntityState &corr
        constexpr float max_disp = 0.0001f; // (1/100)^2
 
        if (disp_squared > warp_thresh) {
-               player_state.chunk_pos = replay.GetState().chunk_pos;
-               player_state.block_pos = replay.GetState().block_pos;
+               player_state.pos = replay.GetState().pos;
        } else if (disp_squared < max_disp) {
-               player_state.block_pos += displacement;
+               player_state.pos.block += displacement;
        } else {
                displacement *= 0.01f / sqrt(disp_squared);
-               player_state.block_pos += displacement;
+               player_state.pos.block += displacement;
        }
        GetPlayer().GetEntity().SetState(player_state);
        SetMovement(restore_movement);
diff --git a/src/geometry/Location.hpp b/src/geometry/Location.hpp
new file mode 100644 (file)
index 0000000..7dbff69
--- /dev/null
@@ -0,0 +1,106 @@
+#ifndef BLANK_GEOMETRY_LOCATION_HPP_
+#define BLANK_GEOMETRY_LOCATION_HPP_
+
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+template<class T>
+struct Location {
+
+       using Coarse = glm::ivec3;
+       using CoarseScalar = int;
+       using Fine = glm::tvec3<T>;
+       using FineScalar = T;
+       using Self = Location<T>;
+
+
+       Coarse chunk;
+       Fine block;
+
+
+       /// scale of coarse vs fine coordinates
+       static constexpr CoarseScalar scale = 16;
+       /// scale with same type as fine
+       static constexpr FineScalar fscale = scale;
+       /// scale in three dimensions
+       static Coarse Extent() noexcept { return Coarse(scale); }
+       /// extent with same type as fine
+       static Fine FExtent() noexcept { return Fine(fscale); }
+
+
+       Location() noexcept : chunk(CoarseScalar(0)), block(FineScalar(0)) { }
+       Location(const Coarse &c, const Fine &b) noexcept : chunk(c), block(b) { }
+
+       /// from absolute position
+       /// not sanitized
+       explicit Location(const Fine &b) noexcept : chunk(CoarseScalar(0)), block(b) { }
+
+       /// make sure fine coordinates are within [0,scale)
+       /// use this if block isn't too far out of range
+       Self &Correct() noexcept;
+       /// use this if block is way out of range
+       Self &Sanitize() noexcept;
+
+       /// resolve absolute position
+       Fine Absolute() const noexcept {
+               return Fine(chunk * Extent()) + block;
+       }
+       /// get location relative to given coarse coordinates
+       Self Relative(const Coarse &reference) const noexcept {
+               return Self(chunk - reference, block);
+       }
+       /// get difference between this and given location
+       /// (= this - given, points from given to this)
+       /// returned location is not sanitized
+       Self Difference(const Location &other) const noexcept {
+               return Self(chunk - other.chunk, block - other.block);
+       }
+
+};
+
+template<class T>
+inline Location<T> &Location<T>::Correct() noexcept {
+       while (block.x >= fscale) {
+               block.x -= fscale;
+               ++chunk.x;
+       }
+       while (block.x < 0) {
+               block.x += fscale;
+               --chunk.x;
+       }
+       while (block.y >= fscale) {
+               block.y -= fscale;
+               ++chunk.y;
+       }
+       while (block.y < 0) {
+               block.y += fscale;
+               --chunk.y;
+       }
+       while (block.z >= fscale) {
+               block.z -= fscale;
+               ++chunk.z;
+       }
+       while (block.z < 0) {
+               block.z += fscale;
+               --chunk.z;
+       }
+       return *this;
+}
+
+template<class T>
+inline Location<T> &Location<T>::Sanitize() noexcept {
+       Coarse diff = Coarse(block) / Extent();
+       chunk += diff;
+       block -= diff * Extent();
+       // may leave negative coordinates in block
+       return Correct();
+}
+
+using ExactLocation = Location<float>;
+using RoughLocation = Location<int>;
+
+}
+
+#endif
index 86532787653d1b24deef8baec749501eab3377ff..8422623f36746262f30e10f0d78942a44f635439 100644 (file)
@@ -125,9 +125,9 @@ void WorldSave::Read(Player &player) const {
                in.ReadIdentifier(name);
                in.Skip(Token::EQUALS);
                if (name == "chunk") {
-                       in.ReadVec(state.chunk_pos);
+                       in.ReadVec(state.pos.chunk);
                } else if (name == "position") {
-                       in.ReadVec(state.block_pos);
+                       in.ReadVec(state.pos.block);
                } else if (name == "orientation") {
                        in.ReadQuat(state.orient);
                } else if (name == "pitch") {
@@ -152,8 +152,8 @@ void WorldSave::Write(const Player &player) const {
        }
        const EntityState &state = player.GetEntity().GetState();
        ofstream out(PlayerPath(player));
-       out << "chunk = " << state.chunk_pos << ';' << endl;
-       out << "position = " << state.block_pos << ';' << endl;
+       out << "chunk = " << state.pos.chunk << ';' << endl;
+       out << "position = " << state.pos.block << ';' << endl;
        out << "orientation = " << state.orient << ';' << endl;
        out << "pitch = " << state.pitch << ';' << endl;
        out << "yaw = " << state.yaw << ';' << endl;
@@ -167,7 +167,7 @@ string WorldSave::PlayerPath(const Player &player) const {
 }
 
 
-bool WorldSave::Exists(const Chunk::Pos &pos) const noexcept {
+bool WorldSave::Exists(const ExactLocation::Coarse &pos) const noexcept {
        return is_file(ChunkPath(pos));
 }
 
@@ -217,7 +217,7 @@ void WorldSave::Write(Chunk &chunk) const {
 }
 
 
-const char *WorldSave::ChunkPath(const Chunk::Pos &pos) const {
+const char *WorldSave::ChunkPath(const ExactLocation::Coarse &pos) const {
        snprintf(chunk_buf.get(), chunk_bufsiz, chunk_path.c_str(), pos.x, pos.y, pos.z);
        return chunk_buf.get();
 }
index d12de952f986ba46fcadaf187f179991de44b5c0..12a56b029fcd5e60220417c359e3989e17e627d0 100644 (file)
@@ -33,10 +33,10 @@ public:
        std::string PlayerPath(const Player &) const;
 
        // single chunk
-       bool Exists(const Chunk::Pos &) const noexcept;
+       bool Exists(const ExactLocation::Coarse &) const noexcept;
        void Read(Chunk &) const;
        void Write(Chunk &) const;
-       const char *ChunkPath(const Chunk::Pos &) const;
+       const char *ChunkPath(const ExactLocation::Coarse &) const;
 
 private:
        std::string root_path;
index 9130e8ecb8a2b646ee5d5ba0a827aac028a7ced0..5d062343b8976f28425b3c45fd7b704dc00e0e2b 100644 (file)
@@ -446,8 +446,8 @@ void Packet::Payload::Read(glm::quat &val, size_t off) const noexcept {
 }
 
 void Packet::Payload::Write(const EntityState &state, size_t off) noexcept {
-       Write(state.chunk_pos, off);
-       WritePackU(state.block_pos * (1.0f / 16.0f), off + 12);
+       Write(state.pos.chunk, off);
+       WritePackU(state.pos.block * (1.0f / ExactLocation::fscale), off + 12);
        Write(state.velocity, off + 18);
        Write(state.orient, off + 30);
        WritePackN(state.pitch * PI_0p5_inv, off + 38);
@@ -455,20 +455,20 @@ void Packet::Payload::Write(const EntityState &state, size_t off) noexcept {
 }
 
 void Packet::Payload::Read(EntityState &state, size_t off) const noexcept {
-       Read(state.chunk_pos, off);
-       ReadPackU(state.block_pos, off + 12);
+       Read(state.pos.chunk, off);
+       ReadPackU(state.pos.block, off + 12);
        Read(state.velocity, off + 18);
        Read(state.orient, off + 30);
        ReadPackN(state.pitch, off + 38);
        ReadPackN(state.yaw, off + 40);
-       state.block_pos *= 16.0f;
+       state.pos.block *= ExactLocation::fscale;
        state.pitch *= PI_0p5;
        state.yaw *= PI;
 }
 
 void Packet::Payload::Write(const EntityState &state, const glm::ivec3 &base, size_t off) noexcept {
-       WritePackB(state.chunk_pos - base, off);
-       WritePackU(state.block_pos * (1.0f / 16.0f), off + 3);
+       WritePackB(state.pos.chunk - base, off);
+       WritePackU(state.pos.block * (1.0f / ExactLocation::fscale), off + 3);
        Write(state.velocity, off + 9);
        Write(state.orient, off + 21);
        WritePackN(state.pitch * PI_0p5_inv, off + 29);
@@ -476,14 +476,14 @@ void Packet::Payload::Write(const EntityState &state, const glm::ivec3 &base, si
 }
 
 void Packet::Payload::Read(EntityState &state, const glm::ivec3 &base, size_t off) const noexcept {
-       ReadPackB(state.chunk_pos, off);
-       ReadPackU(state.block_pos, off + 3);
+       ReadPackB(state.pos.chunk, off);
+       ReadPackU(state.pos.block, off + 3);
        Read(state.velocity, off + 9);
        Read(state.orient, off + 21);
        ReadPackN(state.pitch, off + 29);
        ReadPackN(state.yaw, off + 31);
-       state.chunk_pos += base;
-       state.block_pos *= 16.0f;
+       state.pos.chunk += base;
+       state.pos.block *= ExactLocation::fscale;
        state.pitch *= PI_0p5;
        state.yaw *= PI;
 }
index 8eba85706de21ae7a703930f0544fe3cf35c26e4..9eac33ece573ed35edb24b78893b855f75c0feb3 100644 (file)
@@ -385,9 +385,9 @@ struct QueueCompare {
 
 void ClientConnection::CheckChunkQueue() {
        if (PlayerChunks().Base() != old_base) {
-               Chunk::Pos begin = PlayerChunks().CoordsBegin();
-               Chunk::Pos end = PlayerChunks().CoordsEnd();
-               for (Chunk::Pos pos = begin; pos.z < end.z; ++pos.z) {
+               ExactLocation::Coarse begin = PlayerChunks().CoordsBegin();
+               ExactLocation::Coarse end = PlayerChunks().CoordsEnd();
+               for (ExactLocation::Coarse pos = begin; pos.z < end.z; ++pos.z) {
                        for (pos.y = begin.y; pos.y < end.y; ++pos.y) {
                                for (pos.x = begin.x; pos.x < end.x; ++pos.x) {
                                        if (manhattan_radius(pos - old_base) > PlayerChunks().Extent()) {
@@ -412,7 +412,7 @@ void ClientConnection::CheckChunkQueue() {
                int count = 0;
                constexpr int max = 64;
                while (count < max && !chunk_queue.empty()) {
-                       Chunk::Pos pos = chunk_queue.front();
+                       ExactLocation::Coarse pos = chunk_queue.front();
                        chunk_queue.pop_front();
                        if (PlayerChunks().InRange(pos)) {
                                Chunk *chunk = PlayerChunks().Get(pos);
@@ -434,9 +434,9 @@ void ClientConnection::AttachPlayer(Player &player) {
        PlayerEntity().Ref();
 
        old_base = PlayerChunks().Base();
-       Chunk::Pos begin = PlayerChunks().CoordsBegin();
-       Chunk::Pos end = PlayerChunks().CoordsEnd();
-       for (Chunk::Pos pos = begin; pos.z < end.z; ++pos.z) {
+       ExactLocation::Coarse begin = PlayerChunks().CoordsBegin();
+       ExactLocation::Coarse end = PlayerChunks().CoordsEnd();
+       for (ExactLocation::Coarse pos = begin; pos.z < end.z; ++pos.z) {
                for (pos.y = begin.y; pos.y < end.y; ++pos.y) {
                        for (pos.x = begin.x; pos.x < end.x; ++pos.x) {
                                chunk_queue.push_back(pos);
index aa6629f34741221e9cd45fe10ac71360333e58d3..6942f78704ec3a887dabff21fd35df37e0a6ba51 100644 (file)
@@ -72,12 +72,8 @@ CLI::Command::~Command() {
 
 void TeleportCommand::Execute(CLI &cli, Player &player, TokenStreamReader &args) {
        glm::vec3 pos(args.GetFloat(), args.GetFloat(), args.GetFloat());
-       glm::ivec3 chunk(pos);
-       chunk /= Chunk::Extent();
-       pos -= chunk;
        EntityState state = player.GetEntity().GetState();
-       state.chunk_pos = chunk;
-       state.block_pos = pos;
+       state.pos = ExactLocation(pos).Sanitize();
        player.GetEntity().SetState(state);
 }
 
index 4b4ea210596bbd8d3c6adb0f63e5131cf6065708..ea3de59b206e3ad7b90b9033db7506037bd24692 100644 (file)
@@ -11,7 +11,6 @@ namespace blank {
 struct Block {
 
        using Type = unsigned short;
-       using Pos = glm::vec3;
 
        enum Face {
                FACE_UP,
index 605b896f2553785964c72f55e809c162f72fe8f3..3d7aa0420f442d204311086ac1025e31000697df 100644 (file)
@@ -11,19 +11,19 @@ class BlockLookup {
 
 public:
        /// resolve chunk/position from oob coordinates
-       BlockLookup(Chunk *c, const Chunk::Pos &p) noexcept;
+       BlockLookup(Chunk *c, const RoughLocation::Fine &p) noexcept;
 
        /// resolve chunk/position from ib coordinates and direction
-       BlockLookup(Chunk *c, const Chunk::Pos &p, Block::Face dir) noexcept;
+       BlockLookup(Chunk *c, const RoughLocation::Fine &p, Block::Face dir) noexcept;
 
        /// check if lookup was successful
        operator bool() const { return chunk; }
 
        // only valid if lookup was successful
        Chunk &GetChunk() const noexcept { return *chunk; }
-       const Chunk::Pos &GetBlockPos() const noexcept { return pos; }
+       const RoughLocation::Fine &GetBlockPos() const noexcept { return pos; }
        int GetBlockIndex() const noexcept { return Chunk::ToIndex(pos); }
-       Block::Pos GetBlockCoords() const noexcept { return Chunk::ToCoords(pos); }
+       ExactLocation::Fine GetBlockCoords() const noexcept { return Chunk::ToCoords(pos); }
        const Block &GetBlock() const noexcept { return GetChunk().BlockAt(GetBlockPos()); }
        const BlockType &GetType() const noexcept { return GetChunk().Type(GetBlock()); }
        int GetLight() const noexcept { return GetChunk().GetLight(GetBlockPos()); }
@@ -35,7 +35,7 @@ public:
 
 private:
        Chunk *chunk;
-       Chunk::Pos pos;
+       RoughLocation::Fine pos;
 
 };
 
index b6298b3c7d2239e77c86394b1a7bf7d95bf339dc..6e87a0907d15059cea8b15e5255022b333b4c12b 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "Block.hpp"
 #include "BlockTypeRegistry.hpp"
+#include "../geometry/Location.hpp"
 #include "../geometry/primitive.hpp"
 
 #include <vector>
@@ -18,86 +19,81 @@ class WorldCollision;
 /// cube of size 16 (256 tiles, 4096 blocks)
 class Chunk {
 
-public:
-       using Pos = glm::ivec3;
-
 public:
        explicit Chunk(const BlockTypeRegistry &) noexcept;
 
        Chunk(Chunk &&) noexcept;
        Chunk &operator =(Chunk &&) noexcept;
 
-       static constexpr int width = 16;
-       static constexpr int height = 16;
-       static constexpr int depth = 16;
-       static Pos Extent() noexcept { return { width, height, depth }; }
-       static constexpr int size = width * height * depth;
+       static constexpr int side = ExactLocation::scale;
+       static constexpr float fside = ExactLocation::fscale;
+       static constexpr int size = side * side * side;
 
-       static AABB Bounds() noexcept { return AABB{ { 0, 0, 0 }, Extent() }; }
+       static AABB Bounds() noexcept { return AABB{ { 0.0f, 0.0f, 0.0f }, ExactLocation::FExtent() }; }
 
-       static constexpr bool InBounds(const Block::Pos &pos) noexcept {
+       static constexpr bool InBounds(const ExactLocation::Fine &pos) noexcept {
                return
-                       pos.x >= 0 && pos.x < width &&
-                       pos.y >= 0 && pos.y < height &&
-                       pos.z >= 0 && pos.z < depth;
+                       pos.x >= 0.0f && pos.x < fside &&
+                       pos.y >= 0.0f && pos.y < fside &&
+                       pos.z >= 0.0f && pos.z < fside;
        }
-       static constexpr bool InBounds(const Pos &pos) noexcept {
+       static constexpr bool InBounds(const RoughLocation::Fine &pos) noexcept {
                return
-                       pos.x >= 0 && pos.x < width &&
-                       pos.y >= 0 && pos.y < height &&
-                       pos.z >= 0 && pos.z < depth;
+                       pos.x >= 0 && pos.x < side &&
+                       pos.y >= 0 && pos.y < side &&
+                       pos.z >= 0 && pos.z < side;
        }
-       static constexpr int ToIndex(const Pos &pos) noexcept {
-               return pos.x + pos.y * width + pos.z * width * height;
+       static constexpr int ToIndex(const RoughLocation::Fine &pos) noexcept {
+               return pos.x + pos.y * side + pos.z * side * side;
        }
        static constexpr bool InBounds(int idx) noexcept {
                return idx >= 0 && idx < size;
        }
-       static Block::Pos ToCoords(int idx) noexcept {
-               return Block::Pos(
-                       0.5f + (idx % width),
-                       0.5f + ((idx / width) % height),
-                       0.5f + (idx / (width * height))
+       static ExactLocation::Fine ToCoords(int idx) noexcept {
+               return ExactLocation::Fine(
+                       0.5f + (idx % side),
+                       0.5f + ((idx / side) % side),
+                       0.5f + (idx / (side * side))
                );
        }
-       static Block::Pos ToCoords(const Pos &pos) noexcept {
-               return Block::Pos(pos) + 0.5f;
+       static ExactLocation::Fine ToCoords(const RoughLocation::Fine &pos) noexcept {
+               return ExactLocation::Fine(pos) + 0.5f;
        }
-       static Pos ToPos(int idx) noexcept {
-               return Pos(
-                       (idx % width),
-                       ((idx / width) % height),
-                       (idx / (width * height))
+       static RoughLocation::Fine ToPos(int idx) noexcept {
+               return RoughLocation::Fine(
+                       (idx % side),
+                       ((idx / side) % side),
+                       (idx / (side * side))
                );
        }
-       glm::mat4 ToTransform(const Pos &pos, int idx) const noexcept;
+       glm::mat4 ToTransform(const RoughLocation::Fine &pos, int idx) const noexcept;
 
-       Block::Pos ToSceneCoords(const Pos &base, const Block::Pos &pos) const noexcept {
-               return Block::Pos((position - base) * Extent()) + pos;
+       ExactLocation::Fine ToSceneCoords(const ExactLocation::Coarse &base, const ExactLocation::Fine &pos) const noexcept {
+               return ExactLocation::Fine((position - base) * ExactLocation::Extent()) + pos;
        }
 
-       static bool IsBorder(const Pos &pos) noexcept {
+       static bool IsBorder(const RoughLocation::Fine &pos) noexcept {
                return
                        pos.x == 0 ||
-                       pos.x == width - 1 ||
+                       pos.x == side - 1 ||
                        pos.y == 0 ||
-                       pos.y == height - 1 ||
+                       pos.y == side - 1 ||
                        pos.z == 0 ||
-                       pos.z == depth - 1;
+                       pos.z == side - 1;
        }
        static constexpr bool IsBorder(int idx) noexcept {
                return
-                       idx < width * height ||                    // low Z plane
-                       idx % width == 0 ||                          // low X plane
-                       (idx / (width * height)) == depth - 1 || // high Z plane
-                       idx % width == width - 1 ||                // high X plane
-                       (idx / width) % height == 0 ||             // low Y plane
-                       (idx / width) % height == height - 1;    // high Y plane
+                       idx < side * side ||                 // low Z plane
+                       idx % side == 0 ||                   // low X plane
+                       (idx / (side * side)) == side - 1 || // high Z plane
+                       idx % side == side - 1 ||            // high X plane
+                       (idx / side) % side == 0 ||          // low Y plane
+                       (idx / side) % side == side - 1;     // high Y plane
        }
 
        bool IsSurface(int index) const noexcept { return IsSurface(ToPos(index)); }
-       bool IsSurface(const Block::Pos &pos) const noexcept { return IsSurface(Pos(pos)); }
-       bool IsSurface(const Pos &pos) const noexcept;
+       bool IsSurface(const ExactLocation::Fine &pos) const noexcept { return IsSurface(RoughLocation::Fine(pos)); }
+       bool IsSurface(const RoughLocation::Fine &pos) const noexcept;
 
        void SetNeighbor(Block::Face, Chunk &) noexcept;
        bool HasNeighbor(Block::Face f) const noexcept { return neighbor[f]; }
@@ -106,28 +102,28 @@ public:
        void Unlink() noexcept;
 
        // check which faces of a block at given index are obstructed (and therefore invisible)
-       Block::FaceSet Obstructed(const Pos &) const noexcept;
+       Block::FaceSet Obstructed(const RoughLocation::Fine &) const noexcept;
 
        void SetBlock(int index, const Block &) noexcept;
-       void SetBlock(const Block::Pos &pos, const Block &block) noexcept { SetBlock(ToIndex(pos), block); }
-       void SetBlock(const Pos &pos, const Block &block) noexcept { SetBlock(ToIndex(pos), block); }
+       void SetBlock(const ExactLocation::Fine &pos, const Block &block) noexcept { SetBlock(ToIndex(pos), block); }
+       void SetBlock(const RoughLocation::Fine &pos, const Block &block) noexcept { SetBlock(ToIndex(pos), block); }
 
        const Block &BlockAt(int index) const noexcept { return blocks[index]; }
-       const Block &BlockAt(const Block::Pos &pos) const noexcept { return BlockAt(ToIndex(pos)); }
-       const Block &BlockAt(const Pos &pos) const noexcept { return BlockAt(ToIndex(pos)); }
+       const Block &BlockAt(const ExactLocation::Fine &pos) const noexcept { return BlockAt(ToIndex(pos)); }
+       const Block &BlockAt(const RoughLocation::Fine &pos) const noexcept { return BlockAt(ToIndex(pos)); }
 
        const BlockType &Type(const Block &b) const noexcept { return types->Get(b.type); }
        const BlockType &Type(int index) const noexcept { return Type(BlockAt(index)); }
 
        void SetLight(int index, int level) noexcept;
-       void SetLight(const Pos &pos, int level) noexcept { SetLight(ToIndex(pos), level); }
-       void SetLight(const Block::Pos &pos, int level) noexcept { SetLight(ToIndex(pos), level); }
+       void SetLight(const ExactLocation::Fine &pos, int level) noexcept { SetLight(ToIndex(pos), level); }
+       void SetLight(const RoughLocation::Fine &pos, int level) noexcept { SetLight(ToIndex(pos), level); }
 
        int GetLight(int index) const noexcept;
-       int GetLight(const Pos &pos) const noexcept { return GetLight(ToIndex(pos)); }
-       int GetLight(const Block::Pos &pos) const noexcept { return GetLight(ToIndex(pos)); }
+       int GetLight(const ExactLocation::Fine &pos) const noexcept { return GetLight(ToIndex(pos)); }
+       int GetLight(const RoughLocation::Fine &pos) const noexcept { return GetLight(ToIndex(pos)); }
 
-       float GetVertexLight(const Pos &, const BlockMesh::Position &, const EntityMesh::Normal &) const noexcept;
+       float GetVertexLight(const RoughLocation::Fine &, const BlockMesh::Position &, const EntityMesh::Normal &) const noexcept;
 
        bool Intersection(
                const Ray &ray,
@@ -148,10 +144,10 @@ public:
                const glm::mat4 &Mchunk,
                std::vector<WorldCollision> &) noexcept;
 
-       void Position(const Pos &pos) noexcept { position = pos; }
-       const Pos &Position() const noexcept { return position; }
-       glm::mat4 Transform(const Pos &offset) const noexcept {
-               return glm::translate((position - offset) * Extent());
+       void Position(const ExactLocation::Coarse &pos) noexcept { position = pos; }
+       const ExactLocation::Coarse &Position() const noexcept { return position; }
+       glm::mat4 Transform(const ExactLocation::Coarse &offset) const noexcept {
+               return glm::translate((position - offset) * ExactLocation::Extent());
        }
 
        void *BlockData() noexcept { return &blocks[0]; }
@@ -185,7 +181,7 @@ private:
        bool generated;
        bool lighted;
 
-       Pos position;
+       ExactLocation::Coarse position;
        int ref_count;
        bool dirty_mesh;
        bool dirty_save;
index 97956e9c4d47b434a6f6981bb9908af76724b761..1823de72b154359c4635a7a0e4f0d1d55ae6f9db 100644 (file)
@@ -15,26 +15,26 @@ class ChunkStore;
 class ChunkIndex {
 
 public:
-       ChunkIndex(ChunkStore &, const Chunk::Pos &base, int extent);
+       ChunkIndex(ChunkStore &, const ExactLocation::Coarse &base, int extent);
        ~ChunkIndex();
 
        ChunkIndex(const ChunkIndex &) = delete;
        ChunkIndex &operator =(const ChunkIndex &) = delete;
 
 public:
-       bool InRange(const Chunk::Pos &) const noexcept;
-       bool IsBorder(const Chunk::Pos &) const noexcept;
-       int Distance(const Chunk::Pos &) const noexcept;
+       bool InRange(const ExactLocation::Coarse &) const noexcept;
+       bool IsBorder(const ExactLocation::Coarse &) const noexcept;
+       int Distance(const ExactLocation::Coarse &) const noexcept;
 
-       bool HasAllSurrounding(const Chunk::Pos &) const noexcept;
+       bool HasAllSurrounding(const ExactLocation::Coarse &) const noexcept;
 
-       int IndexOf(const Chunk::Pos &) const noexcept;
-       Chunk::Pos PositionOf(int) const noexcept;
+       int IndexOf(const ExactLocation::Coarse &) const noexcept;
+       ExactLocation::Coarse PositionOf(int) const noexcept;
 
        /// returns nullptr if given position is out of range or the chunk
        /// is not loaded, so also works as a "has" function
-       Chunk *Get(const Chunk::Pos &) noexcept;
-       const Chunk *Get(const Chunk::Pos &) const noexcept;
+       Chunk *Get(const ExactLocation::Coarse &) noexcept;
+       const Chunk *Get(const ExactLocation::Coarse &) const noexcept;
        Chunk *operator [](int i) noexcept { return chunks[i]; }
        const Chunk *operator [](int i) const noexcept { return chunks[i]; }
 
@@ -47,8 +47,8 @@ public:
 
        int Extent() const noexcept { return extent; }
 
-       Chunk::Pos CoordsBegin() const noexcept { return base - Chunk::Pos(extent); }
-       Chunk::Pos CoordsEnd() const noexcept { return base + Chunk::Pos(extent + 1); }
+       ExactLocation::Coarse CoordsBegin() const noexcept { return base - ExactLocation::Coarse(extent); }
+       ExactLocation::Coarse CoordsEnd() const noexcept { return base + ExactLocation::Coarse(extent + 1); }
 
        void Register(Chunk &) noexcept;
 
@@ -56,10 +56,10 @@ public:
        int IndexedChunks() const noexcept { return total_indexed; }
        int MissingChunks() const noexcept { return total_length - total_indexed; }
 
-       Chunk::Pos NextMissing() noexcept;
+       ExactLocation::Coarse NextMissing() noexcept;
 
-       const Chunk::Pos &Base() const noexcept { return base; }
-       void Rebase(const Chunk::Pos &);
+       const ExactLocation::Coarse &Base() const noexcept { return base; }
+       void Rebase(const ExactLocation::Coarse &);
 
 private:
        int GetCol(int) const noexcept;
@@ -74,7 +74,7 @@ private:
 
 private:
        ChunkStore &store;
-       Chunk::Pos base;
+       ExactLocation::Coarse base;
        int extent;
        int side_length;
        int total_length;
index 3bbe96d8ee0068194a2a3f12303f81dc4543bf80..12debef87b8f4997460959c40d5b71f668f91d22 100644 (file)
@@ -20,15 +20,15 @@ public:
        ChunkStore &operator =(const ChunkStore &) = delete;
 
 public:
-       ChunkIndex &MakeIndex(const Chunk::Pos &base, int extent);
+       ChunkIndex &MakeIndex(const ExactLocation::Coarse &base, int extent);
        void UnregisterIndex(ChunkIndex &);
 
-       ChunkIndex *ClosestIndex(const Chunk::Pos &pos);
+       ChunkIndex *ClosestIndex(const ExactLocation::Coarse &pos);
 
        /// returns nullptr if given position is not loaded
-       Chunk *Get(const Chunk::Pos &);
+       Chunk *Get(const ExactLocation::Coarse &);
        /// returns nullptr if given position is not indexed
-       Chunk *Allocate(const Chunk::Pos &);
+       Chunk *Allocate(const ExactLocation::Coarse &);
 
        std::list<Chunk>::iterator begin() noexcept { return loaded.begin(); }
        std::list<Chunk>::iterator end() noexcept { return loaded.end(); }
@@ -44,7 +44,7 @@ public:
 
        /// get coordinates of a missing chunk
        /// this will return garbage if none are actually missing
-       Chunk::Pos NextMissing() noexcept;
+       ExactLocation::Coarse NextMissing() noexcept;
 
        void Clean();
 
index 4b24944b42b7648e2634daf00087b65284aea4bc..2a4ef89093ff848556841669839c95965f7614e3 100644 (file)
@@ -63,11 +63,11 @@ public:
 
        const glm::vec3 &Velocity() const noexcept { return state.velocity; }
 
-       const glm::vec3 &Position() const noexcept { return state.block_pos; }
-       void Position(const glm::ivec3 &, const glm::vec3 &) noexcept;
-       void Position(const glm::vec3 &) noexcept;
+       const ExactLocation::Fine &Position() const noexcept { return state.pos.block; }
+       void Position(const ExactLocation::Coarse &, const ExactLocation::Fine &) noexcept;
+       void Position(const ExactLocation::Fine &) noexcept;
 
-       const glm::ivec3 ChunkCoords() const noexcept { return state.chunk_pos; }
+       const glm::ivec3 ChunkCoords() const noexcept { return state.pos.chunk; }
 
        glm::vec3 AbsolutePosition() const noexcept {
                return state.AbsolutePosition();
@@ -92,7 +92,7 @@ public:
        /// get a transform for this entity's view space relative to reference chunk
        glm::mat4 ViewTransform(const glm::ivec3 &reference) const noexcept;
        /// get a ray in entity's face direction originating from center of vision
-       Ray Aim(const Chunk::Pos &chunk_offset) const noexcept;
+       Ray Aim(const ExactLocation::Coarse &chunk_offset) const noexcept;
 
        /// true if this entity's position will change (significantly) the next update
        bool Moving() const noexcept { return speed > 0.0f; }
index 21b60dd0de105ee27a9b25ece204896f3c89d2dd..ee99c76bd75bef248c655c9673785960ea545b2d 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef BLANK_WORLD_ENTITYSTATE_HPP_
 #define BLANK_WORLD_ENTITYSTATE_HPP_
 
-#include "Chunk.hpp"
+#include "../geometry/Location.hpp"
 
 #include <glm/glm.hpp>
 #include <glm/gtc/quaternion.hpp>
@@ -11,8 +11,7 @@ namespace blank {
 
 struct EntityState {
 
-       glm::ivec3 chunk_pos;
-       glm::vec3 block_pos;
+       ExactLocation pos;
        glm::vec3 velocity;
 
        glm::quat orient;
@@ -21,23 +20,23 @@ struct EntityState {
 
        EntityState();
 
-       /// make sure block_pos is within chunk bounds
+       /// make sure pos.block is within chunk bounds
        void AdjustPosition() noexcept;
        /// make sure pitch and yaw are normalized
        void AdjustHeading() noexcept;
 
        /// get a position vector relative to the (0,0,0) chunk
        glm::vec3 AbsolutePosition() const noexcept {
-               return glm::vec3(chunk_pos * Chunk::Extent()) + block_pos;
+               return pos.Absolute();
        }
        /// get a position vector relative to given reference chunk
        glm::vec3 RelativePosition(const glm::ivec3 &reference) const noexcept {
-               return glm::vec3((chunk_pos - reference) * Chunk::Extent()) + block_pos;
+               return pos.Relative(reference).Absolute();
        }
 
        /// get the difference between this and the given position
        glm::vec3 Diff(const EntityState &other) const noexcept {
-               return RelativePosition(other.chunk_pos) - other.block_pos;
+               return pos.Difference(other.pos).Absolute();
        }
 
        /// get entity state as a matrix tranform relative to given reference chunk
index 88ac1eb674b0c24b5449f206694d0f70043145c4..466139b8943a4fadc1187736e6b29509d462642b 100644 (file)
@@ -50,13 +50,13 @@ void Generator::LoadTypes(const BlockTypeRegistry &reg) {
 }
 
 void Generator::operator ()(Chunk &chunk) const noexcept {
-       Chunk::Pos pos(chunk.Position());
-       glm::vec3 coords(pos * Chunk::Extent());
-       for (int z = 0; z < Chunk::depth; ++z) {
-               for (int y = 0; y < Chunk::height; ++y) {
-                       for (int x = 0; x < Chunk::width; ++x) {
-                               Block::Pos block_pos(x, y, z);
-                               chunk.SetBlock(block_pos, Generate(coords + block_pos));
+       ExactLocation::Coarse pos(chunk.Position());
+       ExactLocation::Fine coords(pos * ExactLocation::Extent());
+       for (int z = 0; z < Chunk::side; ++z) {
+               for (int y = 0; y < Chunk::side; ++y) {
+                       for (int x = 0; x < Chunk::side; ++x) {
+                               ExactLocation::Fine block_pos(x, y, z);
+                               chunk.SetBlock(RoughLocation::Fine(x, y, z), Generate(coords + ExactLocation::Fine(x, y, z)));
                        }
                }
        }
index 273bfa63abc3809a507f77889015f044515b254c..8754d3992e800f518dd9b5db021a8c997a80ec9b 100644 (file)
@@ -49,7 +49,7 @@ public:
        bool Intersection(
                const Ray &,
                const glm::mat4 &M,
-               const Chunk::Pos &reference,
+               const ExactLocation::Coarse &reference,
                WorldCollision &);
 
        /// check if this ray hits an entity
index ac7dbbf9f8982d0d7eed51f38113c84588d53c7f..65cba9de58f5140b664dc2fbbd7e80fed8717957 100644 (file)
@@ -35,10 +35,10 @@ struct WorldCollision {
 
        bool Blocks() const noexcept { return chunk->Type(block).collide_block; }
 
-       const Chunk::Pos &ChunkPos() const noexcept { return GetChunk().Position(); }
+       const ExactLocation::Coarse &ChunkPos() const noexcept { return GetChunk().Position(); }
 
-       glm::ivec3 BlockPos() const noexcept { return Chunk::ToPos(block); }
-       glm::vec3 BlockCoords() const noexcept { return Chunk::ToCoords(block); }
+       RoughLocation::Fine BlockPos() const noexcept { return Chunk::ToPos(block); }
+       ExactLocation::Fine BlockCoords() const noexcept { return Chunk::ToCoords(block); }
        glm::mat4 BlockTransform() const noexcept { return GetChunk().ToTransform(BlockPos(), block); }
 
 };
index 8340d56cd50b1888b6798108fd747d028d3baa37..cedc532aed726a838d2b9ad10b7ea04be16eafb5 100644 (file)
@@ -22,9 +22,7 @@
 
 namespace blank {
 
-constexpr int Chunk::width;
-constexpr int Chunk::height;
-constexpr int Chunk::depth;
+constexpr int Chunk::side;
 constexpr int Chunk::size;
 
 
@@ -76,9 +74,9 @@ namespace {
 struct SetNode {
 
        Chunk *chunk;
-       Chunk::Pos pos;
+       RoughLocation::Fine pos;
 
-       SetNode(Chunk *chunk, Chunk::Pos pos)
+       SetNode(Chunk *chunk, RoughLocation::Fine pos)
        : chunk(chunk), pos(pos) { }
 
        int Get() const noexcept { return chunk->GetLight(pos); }
@@ -104,7 +102,7 @@ struct UnsetNode
 
        int level;
 
-       UnsetNode(Chunk *chunk, Chunk::Pos pos)
+       UnsetNode(Chunk *chunk, RoughLocation::Fine pos)
        : SetNode(chunk, pos), level(Get()) { }
 
        UnsetNode(const SetNode &set)
@@ -195,7 +193,7 @@ void Chunk::SetBlock(int index, const Block &block) noexcept {
        } else if (!new_type.block_light && old_type.block_light) {
                // obstacle removed
                int level = 0;
-               Pos pos(ToPos(index));
+               RoughLocation::Fine pos(ToPos(index));
                for (int face = 0; face < Block::FACE_COUNT; ++face) {
                        BlockLookup next_block(this, pos, Block::Face(face));
                        if (next_block) {
@@ -212,10 +210,10 @@ void Chunk::SetBlock(int index, const Block &block) noexcept {
 
 void Chunk::ScanLights() {
        int idx = 0;
-       Pos pos(0, 0, 0);
-       for (; pos.z < depth; ++pos.z) {
-               for (pos.y = 0; pos.y < height; ++pos.y) {
-                       for (pos.x = 0; pos.x < width; ++pos.x, ++idx) {
+       RoughLocation::Fine pos(0, 0, 0);
+       for (; pos.z < side; ++pos.z) {
+               for (pos.y = 0; pos.y < side; ++pos.y) {
+                       for (pos.x = 0; pos.x < side; ++pos.x, ++idx) {
                                const BlockType &type = Type(blocks[idx]);
                                if (type.luminosity) {
                                        SetLight(idx, type.luminosity);
@@ -254,7 +252,7 @@ int Chunk::GetLight(int index) const noexcept {
        return light[index];
 }
 
-float Chunk::GetVertexLight(const Pos &pos, const BlockMesh::Position &vtx, const EntityMesh::Normal &norm) const noexcept {
+float Chunk::GetVertexLight(const RoughLocation::Fine &pos, const BlockMesh::Position &vtx, const EntityMesh::Normal &norm) const noexcept {
        int index = ToIndex(pos);
        float light = GetLight(index);
 
@@ -344,7 +342,7 @@ float Chunk::GetVertexLight(const Pos &pos, const BlockMesh::Position &vtx, cons
 }
 
 
-bool Chunk::IsSurface(const Pos &pos) const noexcept {
+bool Chunk::IsSurface(const RoughLocation::Fine &pos) const noexcept {
        const Block &block = BlockAt(pos);
        if (!Type(block).visible) {
                return false;
@@ -368,16 +366,16 @@ bool Chunk::Intersection(
        coll.chunk = this;
        coll.block = -1;
        coll.depth = std::numeric_limits<float>::infinity();
-       for (int z = 0; z < depth; ++z) {
-               for (int y = 0; y < height; ++y) {
-                       for (int x = 0; x < width; ++x, ++idx) {
+       for (int z = 0; z < side; ++z) {
+               for (int y = 0; y < side; ++y) {
+                       for (int x = 0; x < side; ++x, ++idx) {
                                const BlockType &type = Type(idx);
                                if (!type.collision || !type.shape) {
                                        continue;
                                }
                                float cur_dist;
                                glm::vec3 cur_norm;
-                               if (type.shape->Intersects(ray, M * ToTransform(Pos(x, y, z), idx), cur_dist, cur_norm)) {
+                               if (type.shape->Intersects(ray, M * ToTransform(RoughLocation::Fine(x, y, z), idx), cur_dist, cur_norm)) {
                                        if (cur_dist < coll.depth) {
                                                coll.block = idx;
                                                coll.depth = cur_dist;
@@ -409,14 +407,14 @@ bool Chunk::Intersection(
        if (!blank::Intersection(box, Mbox, Bounds(), Mchunk, penetration, normal)) {
                return false;
        }
-       for (int idx = 0, z = 0; z < depth; ++z) {
-               for (int y = 0; y < height; ++y) {
-                       for (int x = 0; x < width; ++x, ++idx) {
+       for (int idx = 0, z = 0; z < side; ++z) {
+               for (int y = 0; y < side; ++y) {
+                       for (int x = 0; x < side; ++x, ++idx) {
                                const BlockType &type = Type(idx);
                                if (!type.collision || !type.shape) {
                                        continue;
                                }
-                               if (type.shape->Intersects(Mchunk * ToTransform(Pos(x, y, z), idx), box, Mbox, penetration, normal)) {
+                               if (type.shape->Intersects(Mchunk * ToTransform(RoughLocation::Fine(x, y, z), idx), box, Mbox, penetration, normal)) {
                                        col.emplace_back(this, idx, penetration, normal);
                                        any = true;
                                }
@@ -448,11 +446,11 @@ void Chunk::Update(BlockMesh &model) noexcept {
        if (idx_count > 0) {
                int idx = 0;
                BlockMesh::Index vtx_counter = 0;
-               for (size_t z = 0; z < depth; ++z) {
-                       for (size_t y = 0; y < height; ++y) {
-                               for (size_t x = 0; x < width; ++x, ++idx) {
+               for (size_t z = 0; z < side; ++z) {
+                       for (size_t y = 0; y < side; ++y) {
+                               for (size_t x = 0; x < side; ++x, ++idx) {
                                        const BlockType &type = Type(BlockAt(idx));
-                                       const Pos pos(x, y, z);
+                                       const RoughLocation::Fine pos(x, y, z);
 
                                        if (!type.visible || !type.shape || Obstructed(pos).All()) continue;
 
@@ -476,7 +474,7 @@ void Chunk::Update(BlockMesh &model) noexcept {
        ClearMesh();
 }
 
-Block::FaceSet Chunk::Obstructed(const Pos &pos) const noexcept {
+Block::FaceSet Chunk::Obstructed(const RoughLocation::Fine &pos) const noexcept {
        Block::FaceSet result;
 
        for (int f = 0; f < Block::FACE_COUNT; ++f) {
@@ -490,17 +488,17 @@ Block::FaceSet Chunk::Obstructed(const Pos &pos) const noexcept {
        return result;
 }
 
-glm::mat4 Chunk::ToTransform(const Pos &pos, int idx) const noexcept {
+glm::mat4 Chunk::ToTransform(const RoughLocation::Fine &pos, int idx) const noexcept {
        return glm::translate(ToCoords(pos)) * BlockAt(idx).Transform();
 }
 
 
-BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p) noexcept
+BlockLookup::BlockLookup(Chunk *c, const RoughLocation::Fine &p) noexcept
 : chunk(c), pos(p) {
-       while (pos.x >= Chunk::width) {
+       while (pos.x >= Chunk::side) {
                if (chunk->HasNeighbor(Block::FACE_RIGHT)) {
                        chunk = &chunk->GetNeighbor(Block::FACE_RIGHT);
-                       pos.x -= Chunk::width;
+                       pos.x -= Chunk::side;
                } else {
                        chunk = nullptr;
                        return;
@@ -509,16 +507,16 @@ BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p) noexcept
        while (pos.x < 0) {
                if (chunk->HasNeighbor(Block::FACE_LEFT)) {
                        chunk = &chunk->GetNeighbor(Block::FACE_LEFT);
-                       pos.x += Chunk::width;
+                       pos.x += Chunk::side;
                } else {
                        chunk = nullptr;
                        return;
                }
        }
-       while (pos.y >= Chunk::height) {
+       while (pos.y >= Chunk::side) {
                if (chunk->HasNeighbor(Block::FACE_UP)) {
                        chunk = &chunk->GetNeighbor(Block::FACE_UP);
-                       pos.y -= Chunk::height;
+                       pos.y -= Chunk::side;
                } else {
                        chunk = nullptr;
                        return;
@@ -527,16 +525,16 @@ BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p) noexcept
        while (pos.y < 0) {
                if (chunk->HasNeighbor(Block::FACE_DOWN)) {
                        chunk = &chunk->GetNeighbor(Block::FACE_DOWN);
-                       pos.y += Chunk::height;
+                       pos.y += Chunk::side;
                } else {
                        chunk = nullptr;
                        return;
                }
        }
-       while (pos.z >= Chunk::depth) {
+       while (pos.z >= Chunk::side) {
                if (chunk->HasNeighbor(Block::FACE_FRONT)) {
                        chunk = &chunk->GetNeighbor(Block::FACE_FRONT);
-                       pos.z -= Chunk::depth;
+                       pos.z -= Chunk::side;
                } else {
                        chunk = nullptr;
                        return;
@@ -545,7 +543,7 @@ BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p) noexcept
        while (pos.z < 0) {
                if (chunk->HasNeighbor(Block::FACE_BACK)) {
                        chunk = &chunk->GetNeighbor(Block::FACE_BACK);
-                       pos.z += Chunk::depth;
+                       pos.z += Chunk::side;
                } else {
                        chunk = nullptr;
                        return;
@@ -553,11 +551,11 @@ BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p) noexcept
        }
 }
 
-BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p, Block::Face face) noexcept
+BlockLookup::BlockLookup(Chunk *c, const RoughLocation::Fine &p, Block::Face face) noexcept
 : chunk(c), pos(p) {
        pos += Block::FaceNormal(face);
        if (!Chunk::InBounds(pos)) {
-               pos -= Block::FaceNormal(face) * Chunk::Extent();
+               pos -= Block::FaceNormal(face) * ExactLocation::Extent();
                chunk = &chunk->GetNeighbor(face);
        }
 }
@@ -610,7 +608,7 @@ int ChunkLoader::ToLoad() const noexcept {
 bool ChunkLoader::LoadOne() {
        if (!store.HasMissing()) return false;
 
-       Chunk::Pos pos = store.NextMissing();
+       ExactLocation::Coarse pos = store.NextMissing();
        Chunk *chunk = store.Allocate(pos);
        if (!chunk) {
                // chunk store corrupted?
@@ -630,9 +628,9 @@ bool ChunkLoader::LoadOne() {
                return generated;
        }
 
-       Chunk::Pos begin(pos - Chunk::Pos(1));
-       Chunk::Pos end(pos + Chunk::Pos(2));
-       for (Chunk::Pos iter(begin); iter.z < end.z; ++iter.z) {
+       ExactLocation::Coarse begin(pos - ExactLocation::Coarse(1));
+       ExactLocation::Coarse end(pos + ExactLocation::Coarse(2));
+       for (ExactLocation::Coarse iter(begin); iter.z < end.z; ++iter.z) {
                for (iter.y = begin.y; iter.y < end.y; ++iter.y) {
                        for (iter.x = begin.x; iter.x < end.x; ++iter.x) {
                                if (index->IsBorder(iter)) continue;
@@ -715,7 +713,7 @@ void ChunkRenderer::Render(Viewport &viewport) {
 }
 
 
-ChunkIndex::ChunkIndex(ChunkStore &store, const Chunk::Pos &base, int extent)
+ChunkIndex::ChunkIndex(ChunkStore &store, const ExactLocation::Coarse &base, int extent)
 : store(store)
 , base(base)
 , extent(extent)
@@ -732,22 +730,22 @@ ChunkIndex::~ChunkIndex() {
        Clear();
 }
 
-bool ChunkIndex::InRange(const Chunk::Pos &pos) const noexcept {
+bool ChunkIndex::InRange(const ExactLocation::Coarse &pos) const noexcept {
        return Distance(pos) <= extent;
 }
 
-bool ChunkIndex::IsBorder(const Chunk::Pos &pos) const noexcept {
+bool ChunkIndex::IsBorder(const ExactLocation::Coarse &pos) const noexcept {
        return Distance(pos) == extent;
 }
 
-int ChunkIndex::Distance(const Chunk::Pos &pos) const noexcept {
+int ChunkIndex::Distance(const ExactLocation::Coarse &pos) const noexcept {
        return manhattan_radius(pos - base);
 }
 
-bool ChunkIndex::HasAllSurrounding(const Chunk::Pos &pos) const noexcept {
-       Chunk::Pos begin(pos - Chunk::Pos(1));
-       Chunk::Pos end(pos + Chunk::Pos(2));
-       for (Chunk::Pos iter(begin); iter.z < end.z; ++iter.z) {
+bool ChunkIndex::HasAllSurrounding(const ExactLocation::Coarse &pos) const noexcept {
+       ExactLocation::Coarse begin(pos - ExactLocation::Coarse(1));
+       ExactLocation::Coarse end(pos + ExactLocation::Coarse(2));
+       for (ExactLocation::Coarse iter(begin); iter.z < end.z; ++iter.z) {
                for (iter.y = begin.y; iter.y < end.y; ++iter.y) {
                        for (iter.x = begin.x; iter.x < end.x; ++iter.x) {
                                if (!Get(iter)) return false;
@@ -757,8 +755,8 @@ bool ChunkIndex::HasAllSurrounding(const Chunk::Pos &pos) const noexcept {
        return true;
 }
 
-int ChunkIndex::IndexOf(const Chunk::Pos &pos) const noexcept {
-       Chunk::Pos mod_pos(
+int ChunkIndex::IndexOf(const ExactLocation::Coarse &pos) const noexcept {
+       ExactLocation::Coarse mod_pos(
                GetCol(pos.x),
                GetCol(pos.y),
                GetCol(pos.z)
@@ -768,18 +766,18 @@ int ChunkIndex::IndexOf(const Chunk::Pos &pos) const noexcept {
                +  mod_pos.z * stride.z;
 }
 
-Chunk::Pos ChunkIndex::PositionOf(int i) const noexcept {
-       Chunk::Pos zero_pos(
+ExactLocation::Coarse ChunkIndex::PositionOf(int i) const noexcept {
+       ExactLocation::Coarse zero_pos(
                (i / stride.x) % side_length,
                (i / stride.y) % side_length,
                (i / stride.z) % side_length
        );
-       Chunk::Pos zero_base(
+       ExactLocation::Coarse zero_base(
                GetCol(base.x),
                GetCol(base.y),
                GetCol(base.z)
        );
-       Chunk::Pos base_relative(zero_pos - zero_base);
+       ExactLocation::Coarse base_relative(zero_pos - zero_base);
        if (base_relative.x > extent) base_relative.x -= side_length;
        else if (base_relative.x < -extent) base_relative.x += side_length;
        if (base_relative.y > extent) base_relative.y -= side_length;
@@ -789,7 +787,7 @@ Chunk::Pos ChunkIndex::PositionOf(int i) const noexcept {
        return base + base_relative;
 }
 
-Chunk *ChunkIndex::Get(const Chunk::Pos &pos) noexcept {
+Chunk *ChunkIndex::Get(const ExactLocation::Coarse &pos) noexcept {
        if (InRange(pos)) {
                return chunks[IndexOf(pos)];
        } else {
@@ -797,7 +795,7 @@ Chunk *ChunkIndex::Get(const Chunk::Pos &pos) noexcept {
        }
 }
 
-const Chunk *ChunkIndex::Get(const Chunk::Pos &pos) const noexcept {
+const Chunk *ChunkIndex::Get(const ExactLocation::Coarse &pos) const noexcept {
        if (InRange(pos)) {
                return chunks[IndexOf(pos)];
        } else {
@@ -805,10 +803,10 @@ const Chunk *ChunkIndex::Get(const Chunk::Pos &pos) const noexcept {
        }
 }
 
-void ChunkIndex::Rebase(const Chunk::Pos &new_base) {
+void ChunkIndex::Rebase(const ExactLocation::Coarse &new_base) {
        if (new_base == base) return;
 
-       Chunk::Pos diff(new_base - base);
+       ExactLocation::Coarse diff(new_base - base);
 
        if (manhattan_radius(diff) > extent) {
                // that's more than half, so probably not worth shifting
@@ -907,7 +905,7 @@ void ChunkIndex::Unset(int index) noexcept {
        }
 }
 
-Chunk::Pos ChunkIndex::NextMissing() noexcept {
+ExactLocation::Coarse ChunkIndex::NextMissing() noexcept {
        if (MissingChunks() > 0) {
                int roundtrip = last_missing;
                last_missing = (last_missing + 1) % total_length;
@@ -934,7 +932,7 @@ ChunkStore::~ChunkStore() {
 
 }
 
-ChunkIndex &ChunkStore::MakeIndex(const Chunk::Pos &pos, int extent) {
+ChunkIndex &ChunkStore::MakeIndex(const ExactLocation::Coarse &pos, int extent) {
        indices.emplace_back(*this, pos, extent);
        return indices.back();
 }
@@ -950,7 +948,7 @@ void ChunkStore::UnregisterIndex(ChunkIndex &index) {
        }
 }
 
-ChunkIndex *ChunkStore::ClosestIndex(const Chunk::Pos &pos) {
+ChunkIndex *ChunkStore::ClosestIndex(const ExactLocation::Coarse &pos) {
        ChunkIndex *closest_index = nullptr;
        int closest_distance = std::numeric_limits<int>::max();
 
@@ -965,7 +963,7 @@ ChunkIndex *ChunkStore::ClosestIndex(const Chunk::Pos &pos) {
        return closest_index;
 }
 
-Chunk *ChunkStore::Get(const Chunk::Pos &pos) {
+Chunk *ChunkStore::Get(const ExactLocation::Coarse &pos) {
        for (ChunkIndex &index : indices) {
                Chunk *chunk = index.Get(pos);
                if (chunk) {
@@ -975,7 +973,7 @@ Chunk *ChunkStore::Get(const Chunk::Pos &pos) {
        return nullptr;
 }
 
-Chunk *ChunkStore::Allocate(const Chunk::Pos &pos) {
+Chunk *ChunkStore::Allocate(const ExactLocation::Coarse &pos) {
        Chunk *chunk = Get(pos);
        if (chunk) {
                return chunk;
@@ -995,7 +993,7 @@ Chunk *ChunkStore::Allocate(const Chunk::Pos &pos) {
        }
        for (int i = 0; i < Block::FACE_COUNT; ++i) {
                Block::Face face = Block::Face(i);
-               Chunk::Pos neighbor_pos(pos + Block::FaceNormal(face));
+               ExactLocation::Coarse neighbor_pos(pos + Block::FaceNormal(face));
                Chunk *neighbor = Get(neighbor_pos);
                if (neighbor) {
                        chunk->SetNeighbor(face, *neighbor);
@@ -1021,13 +1019,13 @@ int ChunkStore::EstimateMissing() const noexcept {
        return missing;
 }
 
-Chunk::Pos ChunkStore::NextMissing() noexcept {
+ExactLocation::Coarse ChunkStore::NextMissing() noexcept {
        for (ChunkIndex &index : indices) {
                if (index.MissingChunks()) {
                        return index.NextMissing();
                }
        }
-       return Chunk::Pos(0, 0, 0);
+       return ExactLocation::Coarse(0, 0, 0);
 }
 
 void ChunkStore::Clean() {
index 937203190ff1b3879b64a5339f5e540b222e3ff8..51c060c1c5bf411c183e01da278f2ab12422e451 100644 (file)
@@ -96,12 +96,12 @@ glm::vec3 Entity::ControlForce(const EntityState &s) const noexcept {
 }
 
 void Entity::Position(const glm::ivec3 &c, const glm::vec3 &b) noexcept {
-       state.chunk_pos = c;
-       state.block_pos = b;
+       state.pos.chunk = c;
+       state.pos.block = b;
 }
 
 void Entity::Position(const glm::vec3 &pos) noexcept {
-       state.block_pos = pos;
+       state.pos.block = pos;
        state.AdjustPosition();
 }
 
@@ -115,14 +115,14 @@ void Entity::SetHead(float p, float y) noexcept {
 }
 
 glm::mat4 Entity::Transform(const glm::ivec3 &reference) const noexcept {
-       return glm::translate(glm::vec3((state.chunk_pos - reference) * Chunk::Extent())) * model_transform;
+       return glm::translate(glm::vec3((state.pos.chunk - reference) * ExactLocation::Extent())) * model_transform;
 }
 
 glm::mat4 Entity::ViewTransform(const glm::ivec3 &reference) const noexcept {
        return Transform(reference) * view_transform;
 }
 
-Ray Entity::Aim(const Chunk::Pos &chunk_offset) const noexcept {
+Ray Entity::Aim(const ExactLocation::Coarse &chunk_offset) const noexcept {
        glm::mat4 transform = ViewTransform(chunk_offset);
        return Ray{ glm::vec3(transform[3]), -glm::vec3(transform[2]) };
 }
@@ -138,7 +138,7 @@ void Entity::Update(float dt) {
 
 void Entity::UpdateTransforms() noexcept {
        // model transform is the one given by current state
-       model_transform = state.Transform(state.chunk_pos);
+       model_transform = state.Transform(state.pos.chunk);
        // view transform is either the model's eyes transform or,
        // should the entity have no model, the pitch (yaw already is
        // in model transform)
@@ -268,8 +268,7 @@ bool EntityController::MaxOutForce(
 
 
 EntityState::EntityState()
-: chunk_pos(0)
-, block_pos(0.0f)
+: pos()
 , velocity(0.0f)
 , orient(1.0f, 0.0f, 0.0f, 0.0f)
 , pitch(0.0f)
@@ -278,30 +277,7 @@ EntityState::EntityState()
 }
 
 void EntityState::AdjustPosition() noexcept {
-       while (block_pos.x >= Chunk::width) {
-               block_pos.x -= Chunk::width;
-               ++chunk_pos.x;
-       }
-       while (block_pos.x < 0) {
-               block_pos.x += Chunk::width;
-               --chunk_pos.x;
-       }
-       while (block_pos.y >= Chunk::height) {
-               block_pos.y -= Chunk::height;
-               ++chunk_pos.y;
-       }
-       while (block_pos.y < 0) {
-               block_pos.y += Chunk::height;
-               --chunk_pos.y;
-       }
-       while (block_pos.z >= Chunk::depth) {
-               block_pos.z -= Chunk::depth;
-               ++chunk_pos.z;
-       }
-       while (block_pos.z < 0) {
-               block_pos.z += Chunk::depth;
-               --chunk_pos.z;
-       }
+       pos.Correct();
 }
 
 void EntityState::AdjustHeading() noexcept {
@@ -483,7 +459,7 @@ std::vector<Candidate> candidates;
 bool World::Intersection(
        const Ray &ray,
        const glm::mat4 &M,
-       const Chunk::Pos &reference,
+       const ExactLocation::Coarse &reference,
        WorldCollision &coll
 ) {
        candidates.clear();
@@ -545,7 +521,7 @@ bool World::Intersection(
 
 bool World::Intersection(const Entity &e, const EntityState &s, std::vector<WorldCollision> &col) {
        AABB box = e.Bounds();
-       Chunk::Pos reference = s.chunk_pos;
+       glm::ivec3 reference = s.pos.chunk;
        glm::mat4 M = s.Transform(reference);
        return Intersection(box, M, reference, col);
 }
@@ -603,7 +579,7 @@ void World::Update(Entity &entity, float dt) {
        f.position = sixth * ((a.position + 2.0f * (b.position + c.position)) + d.position);
        f.velocity = sixth * ((a.velocity + 2.0f * (b.velocity + c.velocity)) + d.velocity);
 
-       state.block_pos += f.position * dt;
+       state.pos.block += f.position * dt;
        state.velocity += f.velocity * dt;
        state.AdjustPosition();
 
@@ -617,7 +593,7 @@ EntityDerivative World::CalculateStep(
        const EntityDerivative &delta
 ) {
        EntityState next(cur);
-       next.block_pos += delta.position * dt;
+       next.pos.block += delta.position * dt;
        next.velocity += delta.velocity * dt;
        next.AdjustPosition();
 
diff --git a/tst/geometry/LocationTest.cpp b/tst/geometry/LocationTest.cpp
new file mode 100644 (file)
index 0000000..c21c732
--- /dev/null
@@ -0,0 +1,474 @@
+#include "LocationTest.hpp"
+
+#include <limits>
+#include <glm/gtx/io.hpp>
+
+CPPUNIT_TEST_SUITE_REGISTRATION(blank::test::LocationTest);
+
+
+namespace blank {
+namespace test {
+
+void LocationTest::setUp() {
+}
+
+void LocationTest::tearDown() {
+}
+
+
+void LocationTest::testSanitize() {
+       {
+               RoughLocation loc({ 0, 0, 0 }, { 0, 0, 0 });
+               loc.Sanitize();
+               AssertEqual(
+                       "sanitize rough zero location",
+                       RoughLocation({ 0, 0, 0 }, { 0, 0, 0 }), loc
+               );
+       }
+       {
+               ExactLocation loc({ 0, 0, 0 }, { 0.0f, 0.0f, 0.0f });
+               loc.Sanitize();
+               AssertEqual(
+                       "sanitize exact zero location",
+                       ExactLocation({ 0, 0, 0 }, { 0.0f, 0.0f, 0.0f }), loc
+               );
+       }
+       {
+               ExactLocation loc({ 0, 0, 0 }, { 15.9f, 0.0f, 0.0f });
+               loc.Sanitize();
+               AssertEqual(
+                       "sanitize exact location near upper boundary",
+                       ExactLocation({ 0, 0, 0 }, { 15.9f, 0.0f, 0.0f }), loc
+               );
+       }
+       {
+               RoughLocation loc({ 0, 0, 0 }, { 0, 16, 0 });
+               loc.Sanitize();
+               AssertEqual(
+                       "sanitize rough location",
+                       RoughLocation({ 0, 1, 0 }, { 0, 0, 0 }), loc
+               );
+       }
+       {
+               ExactLocation loc({ 0, 0, 0 }, { 0.0f, 16.0f, 0.0f });
+               loc.Sanitize();
+               AssertEqual(
+                       "sanitize exact location",
+                       ExactLocation({ 0, 1, 0 }, { 0.0f, 0.0f, 0.0f }), loc
+               );
+       }
+       {
+               RoughLocation loc({ 0, 0, 0 }, { 0, 0, -1 });
+               loc.Sanitize();
+               AssertEqual(
+                       "sanitize rough negative location",
+                       RoughLocation({ 0, 0, -1 }, { 0, 0, 15 }), loc
+               );
+       }
+       {
+               ExactLocation loc({ 0, 0, 0 }, { 0.0f, 0.0f, -1.0f });
+               loc.Sanitize();
+               AssertEqual(
+                       "sanitize exact negative location",
+                       ExactLocation({ 0, 0, -1 }, { 0.0f, 0.0f, 15.0f }), loc
+               );
+       }
+       {
+               RoughLocation loc({ 0, 0, 0 }, { 0, 41585, 0 });
+               loc.Sanitize();
+               AssertEqual(
+                       "sanitize rough really far location",
+                       RoughLocation({ 0, 2599, 0 }, { 0, 1, 0 }), loc
+               );
+       }
+       {
+               ExactLocation loc({ 0, 0, 0 }, { 0.0f, 41585.0f, 0.0f });
+               loc.Sanitize();
+               AssertEqual(
+                       "sanitize exact really far location",
+                       ExactLocation({ 0, 2599, 0 }, { 0.0f, 1.0f, 0.0f }), loc
+               );
+       }
+       {
+               RoughLocation loc({ 0, 0, 0 }, { -208005, 0, 0 });
+               loc.Sanitize();
+               AssertEqual(
+                       "sanitize rough really far negative location",
+                       RoughLocation({ -13001, 0, 0 }, { 11, 0, 0 }), loc
+               );
+       }
+       {
+               ExactLocation loc({ 0, 0, 0 }, { -208005.0f, 0.0f, 0.0f });
+               loc.Sanitize();
+               AssertEqual(
+                       "sanitize exact really far negative location",
+                       ExactLocation({ -13001, 0, 0 }, { 11.0f, 0.0f, 0.0f }), loc
+               );
+       }
+       {
+               RoughLocation loc({ 0, -2, 0 }, { 0, 16, 0 });
+               loc.Sanitize();
+               AssertEqual(
+                       "sanitize rough location with non-zero chunk",
+                       RoughLocation({ 0, -1, 0 }, { 0, 0, 0 }), loc
+               );
+       }
+       {
+               ExactLocation loc({ 0, -2, 0 }, { 0.0f, 16.0f, 0.0f });
+               loc.Sanitize();
+               AssertEqual(
+                       "sanitize exact location with non-zero chunk",
+                       ExactLocation({ 0, -1, 0 }, { 0.0f, 0.0f, 0.0f }), loc
+               );
+       }
+       {
+               RoughLocation loc({ 0, 0, 5 }, { 0, 0, -33 });
+               loc.Sanitize();
+               AssertEqual(
+                       "sanitize rough negative location with non-zero chunk",
+                       RoughLocation({ 0, 0, 2 }, { 0, 0, 15 }), loc
+               );
+       }
+       {
+               ExactLocation loc({ 0, 0, 5 }, { 0.0f, 0.0f, -33.0f });
+               loc.Sanitize();
+               AssertEqual(
+                       "sanitize exact negative location with non-zero chunk",
+                       ExactLocation({ 0, 0, 2 }, { 0.0f, 0.0f, 15.0f }), loc
+               );
+       }
+}
+
+void LocationTest::testAbsolute() {
+       {
+               RoughLocation loc({ 0, 0, 0 }, { 0, 0, 0 });
+               AssertEqual(
+                       "absolute of rough zero location",
+                       RoughLocation::Fine(0, 0, 0), loc.Absolute()
+               );
+       }
+       {
+               ExactLocation loc({ 0, 0, 0 }, { 0.0f, 0.0f, 0.0f });
+               AssertEqual(
+                       "absolute of exact zero location",
+                       ExactLocation::Fine(0.0f, 0.0f, 0.0f), loc.Absolute()
+               );
+       }
+       {
+               RoughLocation loc({ 0, 2, 0 }, { 0, 5, 0 });
+               AssertEqual(
+                       "absolute of rough location",
+                       RoughLocation::Fine(0, 37, 0), loc.Absolute()
+               );
+       }
+       {
+               ExactLocation loc({ 0, 2, 0 }, { 0.0f, 5.0f, 0.0f });
+               AssertEqual(
+                       "absolute of exact location",
+                       ExactLocation::Fine(0.0f, 37.0f, 0.0f), loc.Absolute()
+               );
+       }
+       {
+               RoughLocation loc({ 0, 0, -2 }, { 0, 0, 5 });
+               AssertEqual(
+                       "absolute of rough negative location",
+                       RoughLocation::Fine(0, 0, -27), loc.Absolute()
+               );
+       }
+       {
+               ExactLocation loc({ 0, 0, -2 }, { 0.0f, 0.0f, 5.0f });
+               AssertEqual(
+                       "absolute of exact negative location",
+                       ExactLocation::Fine(0.0f, 0.0f, -27.0f), loc.Absolute()
+               );
+       }
+}
+
+void LocationTest::testRelative() {
+       glm::ivec3 base(0, 0, 0);
+       {
+               RoughLocation loc({ 0, 0, 0 }, { 0, 0, 0 });
+               AssertEqual(
+                       "relative of rough zero location with zero base",
+                       RoughLocation::Fine(0, 0, 0), loc.Relative(base).Absolute()
+               );
+       }
+       {
+               ExactLocation loc({ 0, 0, 0 }, { 0.0f, 0.0f, 0.0f });
+               AssertEqual(
+                       "relative of exact zero location with zero base",
+                       ExactLocation::Fine(0.0f, 0.0f, 0.0f), loc.Relative(base).Absolute()
+               );
+       }
+       {
+               RoughLocation loc({ 0, 2, 0 }, { 0, 5, 0 });
+               AssertEqual(
+                       "relative of rough location with zero base",
+                       RoughLocation::Fine(0, 37, 0), loc.Relative(base).Absolute()
+               );
+       }
+       {
+               ExactLocation loc({ 0, 2, 0 }, { 0.0f, 5.0f, 0.0f });
+               AssertEqual(
+                       "relative of exact location with zero base",
+                       ExactLocation::Fine(0.0f, 37.0f, 0.0f), loc.Relative(base).Absolute()
+               );
+       }
+       {
+               RoughLocation loc({ 0, 0, -2 }, { 0, 0, 5 });
+               AssertEqual(
+                       "relative of rough negative location with zero base",
+                       RoughLocation::Fine(0, 0, -27), loc.Relative(base).Absolute()
+               );
+       }
+       {
+               ExactLocation loc({ 0, 0, -2 }, { 0.0f, 0.0f, 5.0f });
+               AssertEqual(
+                       "relative of exact negative location with zero base",
+                       ExactLocation::Fine(0.0f, 0.0f, -27.0f), loc.Relative(base).Absolute()
+               );
+       }
+
+       base = glm::ivec3(0, 1, 0);
+       {
+               RoughLocation loc({ 0, 0, 0 }, { 0, 0, 0 });
+               AssertEqual(
+                       "relative of rough zero location with positive base",
+                       RoughLocation::Fine(0, -16, 0), loc.Relative(base).Absolute()
+               );
+       }
+       {
+               ExactLocation loc({ 0, 0, 0 }, { 0.0f, 0.0f, 0.0f });
+               AssertEqual(
+                       "relative of exact zero location with positive base",
+                       ExactLocation::Fine(0.0f, -16.0f, 0.0f), loc.Relative(base).Absolute()
+               );
+       }
+       {
+               RoughLocation loc({ 0, 2, 0 }, { 0, 5, 0 });
+               AssertEqual(
+                       "relative of rough location with positive base",
+                       RoughLocation::Fine(0, 21, 0), loc.Relative(base).Absolute()
+               );
+       }
+       {
+               ExactLocation loc({ 0, 2, 0 }, { 0.0f, 5.0f, 0.0f });
+               AssertEqual(
+                       "relative of exact location with positive base",
+                       ExactLocation::Fine(0.0f, 21.0f, 0.0f), loc.Relative(base).Absolute()
+               );
+       }
+       {
+               RoughLocation loc({ 0, 0, -2 }, { 0, 0, 5 });
+               AssertEqual(
+                       "relative of rough negative location with positive base",
+                       RoughLocation::Fine(0, -16, -27), loc.Relative(base).Absolute()
+               );
+       }
+       {
+               ExactLocation loc({ 0, 0, -2 }, { 0.0f, 0.0f, 5.0f });
+               AssertEqual(
+                       "relative of exact negative location with positive base",
+                       ExactLocation::Fine(0.0f, -16.0f, -27.0f), loc.Relative(base).Absolute()
+               );
+       }
+
+       base = glm::ivec3(-2, 0, 0);
+       {
+               RoughLocation loc({ 0, 0, 0 }, { 0, 0, 0 });
+               AssertEqual(
+                       "relative of rough zero location with negative base",
+                       RoughLocation::Fine(32, 0, 0), loc.Relative(base).Absolute()
+               );
+       }
+       {
+               ExactLocation loc({ 0, 0, 0 }, { 0.0f, 0.0f, 0.0f });
+               AssertEqual(
+                       "relative of exact zero location with negative base",
+                       ExactLocation::Fine(32.0f, 0.0f, 0.0f), loc.Relative(base).Absolute()
+               );
+       }
+       {
+               RoughLocation loc({ 0, 2, 0 }, { 0, 5, 0 });
+               AssertEqual(
+                       "relative of rough location with negative base",
+                       RoughLocation::Fine(32, 37, 0), loc.Relative(base).Absolute()
+               );
+       }
+       {
+               ExactLocation loc({ 0, 2, 0 }, { 0.0f, 5.0f, 0.0f });
+               AssertEqual(
+                       "relative of exact location with negative base",
+                       ExactLocation::Fine(32.0f, 37.0f, 0.0f), loc.Relative(base).Absolute()
+               );
+       }
+       {
+               RoughLocation loc({ 0, 0, -2 }, { 0, 0, 5 });
+               AssertEqual(
+                       "relative of rough negative location with negative base",
+                       RoughLocation::Fine(32, 0, -27), loc.Relative(base).Absolute()
+               );
+       }
+       {
+               ExactLocation loc({ 0, 0, -2 }, { 0.0f, 0.0f, 5.0f });
+               AssertEqual(
+                       "relative of exact negative location with negative base",
+                       ExactLocation::Fine(32.0f, 0.0f, -27.0f), loc.Relative(base).Absolute()
+               );
+       }
+}
+
+void LocationTest::testDifference() {
+       {
+               RoughLocation a({ 0, 0, 0 }, { 0, 0, 0 });
+               RoughLocation b({ 0, 0, 0 }, { 0, 0, 0 });
+               AssertEqual(
+                       "difference between rough zero locations",
+                       RoughLocation::Fine(0, 0, 0), a.Difference(b).Absolute()
+               );
+       }
+       {
+               ExactLocation a({ 0, 0, 0 }, { 0.0f, 0.0f, 0.0f });
+               ExactLocation b({ 0, 0, 0 }, { 0.0f, 0.0f, 0.0f });
+               AssertEqual(
+                       "difference between exact zero locations",
+                       ExactLocation::Fine(0.0f, 0.0f, 0.0f), a.Difference(b).Absolute()
+               );
+       }
+       {
+               RoughLocation a({ 0, 0, 0 }, { 5, 0, 0 });
+               RoughLocation b({ 0, 0, 0 }, { 0, 0, 0 });
+               AssertEqual(
+                       "difference between rough locations",
+                       RoughLocation::Fine(5, 0, 0), a.Difference(b).Absolute()
+               );
+       }
+       {
+               ExactLocation a({ 0, 0, 0 }, { 5.0f, 0.0f, 0.0f });
+               ExactLocation b({ 0, 0, 0 }, { 0.0f, 0.0f, 0.0f });
+               AssertEqual(
+                       "difference between exact locations",
+                       ExactLocation::Fine(5.0f, 0.0f, 0.0f), a.Difference(b).Absolute()
+               );
+       }
+       {
+               RoughLocation a({ 0, 0, 0 }, { 0, 0, 0 });
+               RoughLocation b({ 0, 0, 0 }, { 0, 5, 0 });
+               AssertEqual(
+                       "difference between rough locations",
+                       RoughLocation::Fine(0, -5, 0), a.Difference(b).Absolute()
+               );
+       }
+       {
+               ExactLocation a({ 0, 0, 0 }, { 0.0f, 0.0f, 0.0f });
+               ExactLocation b({ 0, 0, 0 }, { 0.0f, 5.0f, 0.0f });
+               AssertEqual(
+                       "difference between exact locations",
+                       ExactLocation::Fine(0.0f, -5.0f, 0.0f), a.Difference(b).Absolute()
+               );
+       }
+       {
+               RoughLocation a({ 0, 0, 0 }, { 0, 0, 0 });
+               RoughLocation b({ 0, 0, 0 }, { 0, 0, -3 });
+               AssertEqual(
+                       "difference between rough locations",
+                       RoughLocation::Fine(0, 0, 3), a.Difference(b).Absolute()
+               );
+       }
+       {
+               ExactLocation a({ 0, 0, 0 }, { 0.0f, 0.0f, 0.0f });
+               ExactLocation b({ 0, 0, 0 }, { 0.0f, 0.0f, -3.0f });
+               AssertEqual(
+                       "difference between exact locations",
+                       ExactLocation::Fine(0.0f, 0.0f, 3.0f), a.Difference(b).Absolute()
+               );
+       }
+       {
+               RoughLocation a({ 1, 0, -1 }, { 5, 14, 9 });
+               RoughLocation b({ 0, 2, -1 }, { 3, 2, 0 });
+               AssertEqual(
+                       "difference between rough locations",
+                       RoughLocation::Fine(18, -20, 9), a.Difference(b).Absolute()
+               );
+       }
+       {
+               ExactLocation a({ 1, 0, -1 }, { 5.0f, 14.0f, 9.0f });
+               ExactLocation b({ 0, 2, -1 }, { 3.0f, 2.0f, 0.0f });
+               AssertEqual(
+                       "difference between exact locations",
+                       ExactLocation::Fine(18.0f, -20.0f, 9.0f), a.Difference(b).Absolute()
+               );
+       }
+}
+
+
+void LocationTest::AssertEqual(
+       const std::string &msg,
+       const glm::ivec3 &expected,
+       const glm::ivec3 &actual
+) {
+       CPPUNIT_ASSERT_EQUAL_MESSAGE(
+               msg + " X",
+               expected.x, actual.x
+       );
+       CPPUNIT_ASSERT_EQUAL_MESSAGE(
+               msg + " Y",
+               expected.y, actual.y
+       );
+       CPPUNIT_ASSERT_EQUAL_MESSAGE(
+               msg + " Z",
+               expected.z, actual.z
+       );
+}
+
+void LocationTest::AssertEqual(
+       const std::string &msg,
+       const glm::vec3 &expected,
+       const glm::vec3 &actual
+) {
+       CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
+               msg + " X",
+               expected.x, actual.x, std::numeric_limits<float>::epsilon()
+       );
+       CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
+               msg + " Y",
+               expected.y, actual.y, std::numeric_limits<float>::epsilon()
+       );
+       CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
+               msg + " Z",
+               expected.z, actual.z, std::numeric_limits<float>::epsilon()
+       );
+}
+
+void LocationTest::AssertEqual(
+       const std::string &msg,
+       const RoughLocation &expected,
+       const RoughLocation &actual
+) {
+       AssertEqual(
+               msg + ": bad chunk",
+               expected.chunk, actual.chunk
+       );
+       AssertEqual(
+               msg + ": bad block",
+               expected.block, actual.block
+       );
+}
+
+void LocationTest::AssertEqual(
+       const std::string &msg,
+       const ExactLocation &expected,
+       const ExactLocation &actual
+) {
+       AssertEqual(
+               msg + ": bad chunk",
+               expected.chunk, actual.chunk
+       );
+       AssertEqual(
+               msg + ": bad block",
+               expected.block, actual.block
+       );
+}
+
+}
+}
diff --git a/tst/geometry/LocationTest.hpp b/tst/geometry/LocationTest.hpp
new file mode 100644 (file)
index 0000000..d46be0b
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef BLANK_TEST_GEOMETRY_LOCATIONTEST_HPP_
+#define BLANK_TEST_GEOMETRY_LOCATIONTEST_HPP_
+
+#include "geometry/Location.hpp"
+
+#include <string>
+#include <glm/glm.hpp>
+#include <cppunit/extensions/HelperMacros.h>
+
+
+namespace blank {
+namespace test {
+
+class LocationTest
+: public CppUnit::TestFixture {
+
+CPPUNIT_TEST_SUITE(LocationTest);
+
+CPPUNIT_TEST(testSanitize);
+CPPUNIT_TEST(testAbsolute);
+CPPUNIT_TEST(testRelative);
+CPPUNIT_TEST(testDifference);
+
+CPPUNIT_TEST_SUITE_END();
+
+public:
+       void setUp();
+       void tearDown();
+
+       void testSanitize();
+       void testAbsolute();
+       void testRelative();
+       void testDifference();
+
+private:
+       static void AssertEqual(
+               const std::string &msg,
+               const glm::ivec3 &expected,
+               const glm::ivec3 &actual);
+       static void AssertEqual(
+               const std::string &msg,
+               const glm::vec3 &expected,
+               const glm::vec3 &actual);
+       static void AssertEqual(
+               const std::string &msg,
+               const RoughLocation &expected,
+               const RoughLocation &actual);
+       static void AssertEqual(
+               const std::string &msg,
+               const ExactLocation &expected,
+               const ExactLocation &actual);
+
+};
+
+}
+}
+
+#endif
index 695010dea4db7efb1246dded120ab80e2bb5eef8..86517ca32b7c4d3abf3543e4b74c289bb0fa418a 100644 (file)
@@ -114,8 +114,7 @@ void PacketTest::testJoin() {
        Entity write_entity;
        write_entity.ID(534574);
        EntityState write_state;
-       write_state.chunk_pos = { 7, 2, -3 };
-       write_state.block_pos = { 1.5f, 0.9f, 12.0f };
+       write_state.pos = { { 7, 2, -3 }, { 1.5f, 0.9f, 12.0f } };
        write_state.velocity = { 0.025f, 0.001f, 0.0f };
        write_state.orient = { 1.0f, 0.0f, 0.0f, 0.0f };
        write_state.pitch = 0.3f;
@@ -164,8 +163,7 @@ void PacketTest::testPlayerUpdate() {
        AssertPacket("PlayerUpdate", 4, 50, pack);
 
        EntityState write_state;
-       write_state.chunk_pos = { 7, 2, -3 };
-       write_state.block_pos = { 1.5f, 0.9f, 12.0f };
+       write_state.pos = { { 7, 2, -3 }, { 1.5f, 0.9f, 12.0f } };
        write_state.velocity = { 0.025f, 0.001f, 0.0f };
        write_state.orient = { 1.0f, 0.0f, 0.0f, 0.0f };
        glm::vec3 write_movement(0.5f, -1.0f, 1.0f);
@@ -213,8 +211,7 @@ void PacketTest::testSpawnEntity() {
        model.Enumerate();
        model.Instantiate(write_entity.GetModel());
        EntityState write_state;
-       write_state.chunk_pos = { 7, 2, -3 };
-       write_state.block_pos = { 1.5f, 0.9f, 12.0f };
+       write_state.pos = { { 7, 2, -3 }, { 1.5f, 0.9f, 12.0f } };
        write_state.velocity = { 0.025f, 0.001f, 0.0f };
        write_state.pitch = 0.3f;
        write_state.yaw = -2.3f;
@@ -304,8 +301,7 @@ void PacketTest::testEntityUpdate() {
        Entity write_entity;
        write_entity.ID(8567234);
        EntityState write_state;
-       write_state.chunk_pos = { 7, 2, -3 };
-       write_state.block_pos = { 1.5f, 0.9f, 12.0f };
+       write_state.pos = { { 7, 2, -3 }, { 1.5f, 0.9f, 12.0f } };
        write_state.velocity = { 0.025f, 0.001f, 0.0f };
        write_state.pitch = 0.3f;
        write_state.yaw = -2.3f;
@@ -343,8 +339,7 @@ void PacketTest::testPlayerCorrection() {
 
        Entity write_entity;
        EntityState write_state;
-       write_state.chunk_pos = { 7, 2, -3 };
-       write_state.block_pos = { 1.5f, 0.9f, 12.0f };
+       write_state.pos = { { 7, 2, -3 }, { 1.5f, 0.9f, 12.0f } };
        write_state.velocity = { 0.025f, 0.001f, 0.0f };
        write_state.pitch = 0.3f;
        write_state.yaw = -2.3f;
@@ -595,11 +590,11 @@ void PacketTest::AssertEqual(
 ) {
        AssertEqual(
                message + ": bad chunk position",
-               expected.chunk_pos, actual.chunk_pos
+               expected.pos.chunk, actual.pos.chunk
        );
        AssertEqual(
                message + ": bad block position",
-               expected.block_pos, actual.block_pos, 16.0f/65535.0f // that's about the max accuracy that packing's going to give us
+               expected.pos.block, actual.pos.block, 16.0f/65535.0f // that's about the max accuracy that packing's going to give us
        );
        AssertEqual(
                message + ": bad velocity",
index c40f9da917a979abcc79d62f6c766a6bc5e5a4cb..46933e96e6d724774ddca1c8baa70c9d9ff39715 100644 (file)
@@ -35,259 +35,259 @@ void ChunkTest::tearDown() {
 void ChunkTest::testBounds() {
        CPPUNIT_ASSERT_MESSAGE(
                "valid position out of bounds",
-               Chunk::InBounds(Chunk::Pos(0, 0, 0))
+               Chunk::InBounds(RoughLocation::Fine(0, 0, 0))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "valid position out of bounds",
-               Chunk::InBounds(Chunk::Pos(15, 0, 0))
+               Chunk::InBounds(RoughLocation::Fine(15, 0, 0))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "valid position out of bounds",
-               Chunk::InBounds(Chunk::Pos(0, 15, 0))
+               Chunk::InBounds(RoughLocation::Fine(0, 15, 0))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "valid position out of bounds",
-               Chunk::InBounds(Chunk::Pos(0, 0, 15))
+               Chunk::InBounds(RoughLocation::Fine(0, 0, 15))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "valid position out of bounds",
-               Chunk::InBounds(Chunk::Pos(15, 15, 15))
+               Chunk::InBounds(RoughLocation::Fine(15, 15, 15))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "invalid position in bounds",
-               !Chunk::InBounds(Chunk::Pos(-1, -1, -1))
+               !Chunk::InBounds(RoughLocation::Fine(-1, -1, -1))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "invalid position in bounds",
-               !Chunk::InBounds(Chunk::Pos(-1, 1, 0))
+               !Chunk::InBounds(RoughLocation::Fine(-1, 1, 0))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "invalid position in bounds",
-               !Chunk::InBounds(Chunk::Pos(16, -16, 0))
+               !Chunk::InBounds(RoughLocation::Fine(16, -16, 0))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "invalid position in bounds",
-               !Chunk::InBounds(Chunk::Pos(16, 16, 16))
+               !Chunk::InBounds(RoughLocation::Fine(16, 16, 16))
        );
 }
 
 void ChunkTest::testBorder() {
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(0, 0, 0))
+               Chunk::IsBorder(RoughLocation::Fine(0, 0, 0))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(0, 0, 8))
+               Chunk::IsBorder(RoughLocation::Fine(0, 0, 8))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(0, 0, 15))
+               Chunk::IsBorder(RoughLocation::Fine(0, 0, 15))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(0, 8, 0))
+               Chunk::IsBorder(RoughLocation::Fine(0, 8, 0))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(0, 8, 8))
+               Chunk::IsBorder(RoughLocation::Fine(0, 8, 8))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(0, 8, 15))
+               Chunk::IsBorder(RoughLocation::Fine(0, 8, 15))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(0, 15, 0))
+               Chunk::IsBorder(RoughLocation::Fine(0, 15, 0))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(0, 15, 8))
+               Chunk::IsBorder(RoughLocation::Fine(0, 15, 8))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(0, 15, 15))
+               Chunk::IsBorder(RoughLocation::Fine(0, 15, 15))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(8, 0, 0))
+               Chunk::IsBorder(RoughLocation::Fine(8, 0, 0))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(8, 0, 8))
+               Chunk::IsBorder(RoughLocation::Fine(8, 0, 8))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(8, 0, 15))
+               Chunk::IsBorder(RoughLocation::Fine(8, 0, 15))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(8, 8, 0))
+               Chunk::IsBorder(RoughLocation::Fine(8, 8, 0))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position is border",
-               !Chunk::IsBorder(Chunk::Pos(8, 8, 8))
+               !Chunk::IsBorder(RoughLocation::Fine(8, 8, 8))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(8, 8, 15))
+               Chunk::IsBorder(RoughLocation::Fine(8, 8, 15))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(8, 15, 0))
+               Chunk::IsBorder(RoughLocation::Fine(8, 15, 0))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(8, 15, 8))
+               Chunk::IsBorder(RoughLocation::Fine(8, 15, 8))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(8, 15, 15))
+               Chunk::IsBorder(RoughLocation::Fine(8, 15, 15))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(15, 0, 0))
+               Chunk::IsBorder(RoughLocation::Fine(15, 0, 0))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(15, 0, 8))
+               Chunk::IsBorder(RoughLocation::Fine(15, 0, 8))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(15, 0, 15))
+               Chunk::IsBorder(RoughLocation::Fine(15, 0, 15))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(15, 8, 0))
+               Chunk::IsBorder(RoughLocation::Fine(15, 8, 0))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(15, 8, 8))
+               Chunk::IsBorder(RoughLocation::Fine(15, 8, 8))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(15, 8, 15))
+               Chunk::IsBorder(RoughLocation::Fine(15, 8, 15))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(15, 15, 0))
+               Chunk::IsBorder(RoughLocation::Fine(15, 15, 0))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(15, 15, 8))
+               Chunk::IsBorder(RoughLocation::Fine(15, 15, 8))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::Pos(15, 15, 15))
+               Chunk::IsBorder(RoughLocation::Fine(15, 15, 15))
        );
 
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(0, 0, 0)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(0, 0, 0)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(0, 0, 8)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(0, 0, 8)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(0, 0, 15)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(0, 0, 15)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(0, 8, 0)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(0, 8, 0)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(0, 8, 8)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(0, 8, 8)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(0, 8, 15)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(0, 8, 15)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(0, 15, 0)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(0, 15, 0)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(0, 15, 8)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(0, 15, 8)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(0, 15, 15)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(0, 15, 15)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(8, 0, 0)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(8, 0, 0)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(8, 0, 8)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(8, 0, 8)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(8, 0, 15)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(8, 0, 15)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(8, 8, 0)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(8, 8, 0)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position is border",
-               !Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(8, 8, 8)))
+               !Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(8, 8, 8)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(8, 8, 15)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(8, 8, 15)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(8, 15, 0)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(8, 15, 0)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(8, 15, 8)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(8, 15, 8)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(8, 15, 15)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(8, 15, 15)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(15, 0, 0)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(15, 0, 0)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(15, 0, 8)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(15, 0, 8)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(15, 0, 15)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(15, 0, 15)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(15, 8, 0)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(15, 8, 0)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(15, 8, 8)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(15, 8, 8)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(15, 8, 15)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(15, 8, 15)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(15, 15, 0)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(15, 15, 0)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(15, 15, 8)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(15, 15, 8)))
        );
        CPPUNIT_ASSERT_MESSAGE(
                "position not border",
-               Chunk::IsBorder(Chunk::ToIndex(Chunk::Pos(15, 15, 15)))
+               Chunk::IsBorder(Chunk::ToIndex(RoughLocation::Fine(15, 15, 15)))
        );
 }
 
@@ -384,139 +384,139 @@ void ChunkTest::testLightPropagation() {
        chunk->ScanLights();
 
        // 0 air, 1 solid, 2 solid and emits light level of 5
-       chunk->SetBlock(Chunk::Pos(7, 7, 7), Block(2));
+       chunk->SetBlock(RoughLocation::Fine(7, 7, 7), Block(2));
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "adding luminant block did not set correct light level",
-               5, chunk->GetLight(Chunk::Pos(7, 7, 7))
+               5, chunk->GetLight(RoughLocation::Fine(7, 7, 7))
        );
 
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "light did not propagate correctly in +X",
-               4, chunk->GetLight(Chunk::Pos(8, 7, 7))
+               4, chunk->GetLight(RoughLocation::Fine(8, 7, 7))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "light did not propagate correctly in -X",
-               4, chunk->GetLight(Chunk::Pos(6, 7, 7))
+               4, chunk->GetLight(RoughLocation::Fine(6, 7, 7))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "light did not propagate correctly in +Y",
-               4, chunk->GetLight(Chunk::Pos(7, 8, 7))
+               4, chunk->GetLight(RoughLocation::Fine(7, 8, 7))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "light did not propagate correctly in -Y",
-               4, chunk->GetLight(Chunk::Pos(7, 6, 7))
+               4, chunk->GetLight(RoughLocation::Fine(7, 6, 7))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "light did not propagate correctly in +Z",
-               4, chunk->GetLight(Chunk::Pos(7, 7, 8))
+               4, chunk->GetLight(RoughLocation::Fine(7, 7, 8))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "light did not propagate correctly in -Z",
-               4, chunk->GetLight(Chunk::Pos(7, 7, 6))
+               4, chunk->GetLight(RoughLocation::Fine(7, 7, 6))
        );
 
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "light did not propagate correctly in 2D diagonal",
-               3, chunk->GetLight(Chunk::Pos(8, 8, 7))
+               3, chunk->GetLight(RoughLocation::Fine(8, 8, 7))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "light did not propagate correctly in 2D diagonal",
-               3, chunk->GetLight(Chunk::Pos(7, 6, 8))
+               3, chunk->GetLight(RoughLocation::Fine(7, 6, 8))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "light did not propagate correctly in 2D diagonal",
-               3, chunk->GetLight(Chunk::Pos(6, 7, 8))
+               3, chunk->GetLight(RoughLocation::Fine(6, 7, 8))
        );
 
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "light did not propagate correctly in 3D diagonal",
-               2, chunk->GetLight(Chunk::Pos(8, 6, 6))
+               2, chunk->GetLight(RoughLocation::Fine(8, 6, 6))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "light did not propagate correctly in 3D diagonal",
-               2, chunk->GetLight(Chunk::Pos(6, 6, 8))
+               2, chunk->GetLight(RoughLocation::Fine(6, 6, 8))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "light did not propagate correctly in 3D diagonal",
-               2, chunk->GetLight(Chunk::Pos(6, 8, 8))
+               2, chunk->GetLight(RoughLocation::Fine(6, 8, 8))
        );
 
        // now block the light to the left
-       chunk->SetBlock(Chunk::Pos(6, 7, 7), Block(1));
+       chunk->SetBlock(RoughLocation::Fine(6, 7, 7), Block(1));
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "adding obstacle affected unrelated index",
-               5, chunk->GetLight(Chunk::Pos(7, 7, 7))
+               5, chunk->GetLight(RoughLocation::Fine(7, 7, 7))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "adding obstacle affected unrelated index",
-               4, chunk->GetLight(Chunk::Pos(8, 7, 7))
+               4, chunk->GetLight(RoughLocation::Fine(8, 7, 7))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "adding obstacle affected unrelated index",
-               4, chunk->GetLight(Chunk::Pos(7, 8, 7))
+               4, chunk->GetLight(RoughLocation::Fine(7, 8, 7))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "adding obstacle affected unrelated index",
-               4, chunk->GetLight(Chunk::Pos(7, 6, 7))
+               4, chunk->GetLight(RoughLocation::Fine(7, 6, 7))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "adding obstacle affected unrelated index",
-               4, chunk->GetLight(Chunk::Pos(7, 7, 8))
+               4, chunk->GetLight(RoughLocation::Fine(7, 7, 8))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "adding obstacle affected unrelated index",
-               4, chunk->GetLight(Chunk::Pos(7, 7, 6))
+               4, chunk->GetLight(RoughLocation::Fine(7, 7, 6))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "adding obstacle affected unrelated index",
-               3, chunk->GetLight(Chunk::Pos(6, 6, 7))
+               3, chunk->GetLight(RoughLocation::Fine(6, 6, 7))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "adding obstacle affected unrelated index",
-               3, chunk->GetLight(Chunk::Pos(6, 8, 7))
+               3, chunk->GetLight(RoughLocation::Fine(6, 8, 7))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "adding obstacle affected unrelated index",
-               3, chunk->GetLight(Chunk::Pos(6, 7, 6))
+               3, chunk->GetLight(RoughLocation::Fine(6, 7, 6))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "adding obstacle affected unrelated index",
-               3, chunk->GetLight(Chunk::Pos(6, 7, 6))
+               3, chunk->GetLight(RoughLocation::Fine(6, 7, 6))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "adding obstacle affected unrelated index",
-               2, chunk->GetLight(Chunk::Pos(5, 6, 7))
+               2, chunk->GetLight(RoughLocation::Fine(5, 6, 7))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "adding obstacle affected unrelated index",
-               2, chunk->GetLight(Chunk::Pos(5, 8, 7))
+               2, chunk->GetLight(RoughLocation::Fine(5, 8, 7))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "adding obstacle affected unrelated index",
-               2, chunk->GetLight(Chunk::Pos(5, 7, 6))
+               2, chunk->GetLight(RoughLocation::Fine(5, 7, 6))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "adding obstacle affected unrelated index",
-               2, chunk->GetLight(Chunk::Pos(5, 7, 6))
+               2, chunk->GetLight(RoughLocation::Fine(5, 7, 6))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "adding obstacle resulted in unexpected light level behind it",
-               1, chunk->GetLight(Chunk::Pos(5, 7, 7))
+               1, chunk->GetLight(RoughLocation::Fine(5, 7, 7))
        );
 
        // and remove it again
-       chunk->SetBlock(Chunk::Pos(6, 7, 7), Block(0));
+       chunk->SetBlock(RoughLocation::Fine(6, 7, 7), Block(0));
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "removing obstacle did not refill light correctly",
-               4, chunk->GetLight(Chunk::Pos(6, 7, 7))
+               4, chunk->GetLight(RoughLocation::Fine(6, 7, 7))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "removing obstacle did not refill light correctly",
-               3, chunk->GetLight(Chunk::Pos(5, 7, 7))
+               3, chunk->GetLight(RoughLocation::Fine(5, 7, 7))
        );
        CPPUNIT_ASSERT_EQUAL_MESSAGE(
                "removing obstacle did not refill light correctly",
-               2, chunk->GetLight(Chunk::Pos(4, 7, 7))
+               2, chunk->GetLight(RoughLocation::Fine(4, 7, 7))
        );
 }