summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/Math/Mod.hpp12
-rw-r--r--src/World/ChunkIndex.hpp8
-rw-r--r--src/World/ChunkRegistry.cpp9
-rw-r--r--src/World/ChunkRegistry.hpp2
-rw-r--r--src/World/Generation/ChunkMeshing.cpp12
-rw-r--r--src/World/Generation/ChunkMeshing.hpp8
-rw-r--r--src/World/Position.hpp66
-rw-r--r--src/World/World.cpp7
9 files changed, 86 insertions, 39 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index de94623..c828d5e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -58,6 +58,7 @@ add_executable(meowcraft
     src/World/ChunkRegistry.cpp src/World/ChunkRegistry.hpp
     src/World/Position.hpp
     src/World/ChunkDimensions.hpp
+    src/Math/Mod.hpp
 )
 target_link_libraries(meowcraft glfw GLEW::GLEW)
 
diff --git a/src/Math/Mod.hpp b/src/Math/Mod.hpp
new file mode 100644
index 0000000..d686ad9
--- /dev/null
+++ b/src/Math/Mod.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+namespace Math {
+
+// Returns the least nonnegative remainder of a % b.
+// Euclidian definition of modulo.
+template <typename A, typename B>
+auto mod(A a, B b) -> decltype(a % b) {
+    return (a % b + b) % b;
+}
+
+}
\ No newline at end of file
diff --git a/src/World/ChunkIndex.hpp b/src/World/ChunkIndex.hpp
index ed3347b..70de99f 100644
--- a/src/World/ChunkIndex.hpp
+++ b/src/World/ChunkIndex.hpp
@@ -16,11 +16,17 @@ struct ChunkIndex {
         return {(x + 0.5f) * Width, Height / 2.0f, (y + 0.5f) * Width};
     }
 
-    Position::BlockWorld world(Position::BlockLocal local) const {
+    Position::BlockWorld local_to_world_position(Position::BlockLocal local) const {
         using namespace ChunkDimensions;
         return {x * Width + local.x(), local.y(), y * Width + local.z()};
     }
 
+    static ChunkIndex from_position(Position::BlockWorld pos) {
+        I32 chunk_x = std::round(pos.x() / ChunkDimensions::Width);
+        I32 chunk_y = std::round(pos.z() / ChunkDimensions::Width);
+        return {chunk_x, chunk_y};
+    }
+
     I32 x, y;
 };
 
diff --git a/src/World/ChunkRegistry.cpp b/src/World/ChunkRegistry.cpp
index 41fd1b0..97896fe 100644
--- a/src/World/ChunkRegistry.cpp
+++ b/src/World/ChunkRegistry.cpp
@@ -15,14 +15,11 @@ ChunkRegistry::Data& ChunkRegistry::get(ChunkIndex index) {
 }
 
 ChunkRegistry::Data& ChunkRegistry::find(Position::BlockWorld pos) {
-    return get({
-        static_cast<I32>(pos.x() / Chunk::Width),
-        static_cast<I32>(pos.z() / Chunk::Width)
-    });
+    return get(ChunkIndex::from_position(pos));
 }
 
-ChunkRegistry::Data& ChunkRegistry::find(ChunkIndex chunk, Position::BlockLocal pos) {
-    return find(chunk.world(pos));
+ChunkRegistry::Data& ChunkRegistry::find(Position::World pos) {
+    return find(pos.round_to_block());
 }
 
 }
\ No newline at end of file
diff --git a/src/World/ChunkRegistry.hpp b/src/World/ChunkRegistry.hpp
index 2c515c5..ea887e4 100644
--- a/src/World/ChunkRegistry.hpp
+++ b/src/World/ChunkRegistry.hpp
@@ -41,7 +41,7 @@ public:
     Data& get(ChunkIndex index);
 
     Data& find(Position::BlockWorld pos);
-    Data& find(ChunkIndex chunk, Position::BlockLocal pos);
+    Data& find(Position::World pos);
 private:
     std::unordered_map<ChunkIndex, Data> m_chunks;
 };
diff --git a/src/World/Generation/ChunkMeshing.cpp b/src/World/Generation/ChunkMeshing.cpp
index 3855de4..feecc64 100644
--- a/src/World/Generation/ChunkMeshing.cpp
+++ b/src/World/Generation/ChunkMeshing.cpp
@@ -11,15 +11,15 @@ ChunkMesh mesh_chunk(Chunk& chunk, const SurroundingContext& context) {
     };
 }
 
-SurroundingContext::Block& SurroundingContext::at(Position::BlockOffset p) {
+SurroundingContext::Block& SurroundingContext::at(Position::BlockLocalOffset p) {
     return m_blocks[pos(p)];
 }
 
-const SurroundingContext::Block& SurroundingContext::at(Position::BlockOffset p) const {
+const SurroundingContext::Block& SurroundingContext::at(Position::BlockLocalOffset p) const {
     return m_blocks[pos(p)];
 }
 
-USize SurroundingContext::pos(Position::BlockOffset p) {
+USize SurroundingContext::pos(Position::BlockLocalOffset p) {
     // First we calculate the index as if there were no gaps.
     USize pos = 0;
     pos += p.x() + 1;
@@ -39,7 +39,7 @@ SurroundingContext create_meshing_context(const Chunk& chunk, ChunkNeighbors& ne
     for (I16 x = -1; x <= (I16)Chunk::Width; x++) {
         for (I16 z = -1; z <= (I16)Chunk::Width; z++) {
             for (I16 y = 0; y < (I16)Chunk::Height; y++) {
-                Position::BlockOffset pos{x, y, z};
+                Position::BlockLocalOffset pos{x, y, z};
                 if (pos.fits_within_chunk()) continue;
                 auto [does_exist, block] = get_block_wrapping(chunk, neighbors, {pos.x(), pos.y(), pos.z()});
                 context.at(pos) = {does_exist, block};
@@ -205,7 +205,7 @@ Face<AO> DefaultMeshDecisions::face_ao_values(Chunk& chunk, const SurroundingCon
         auto b = offsets[++offset_index]; // corner
         auto c = offsets[++offset_index % 8];
 
-        auto p = [=](auto o) -> Position::BlockOffset { return {(I16)((I16)x + o.x()), (I16)((I16)y + o.y()), (I16)((I16)z + o.z())}; };
+        auto p = [=](auto o) -> Position::BlockLocalOffset { return {(I16)x + o.x(), (I16)y + o.y(), (I16)z + o.z()}; };
         auto block_a = get_block_from_chunk_or_context(chunk, context, p(a));
         auto block_b = get_block_from_chunk_or_context(chunk, context, p(b));
         auto block_c = get_block_from_chunk_or_context(chunk, context, p(c));
@@ -236,7 +236,7 @@ Vector<3, I32> DefaultMeshDecisions::get_face_normal(BlockSide side) {
 }
 
 SurroundingContext::Block DefaultMeshDecisions::get_block_from_chunk_or_context(
-    const Chunk& chunk, const SurroundingContext& context, Position::BlockOffset pos
+    const Chunk& chunk, const SurroundingContext& context, Position::BlockLocalOffset pos
 ) {
     if (pos.fits_within_chunk()) return {true, chunk.at(pos)};
     return context.at(pos);
diff --git a/src/World/Generation/ChunkMeshing.hpp b/src/World/Generation/ChunkMeshing.hpp
index 5401580..c745a3b 100644
--- a/src/World/Generation/ChunkMeshing.hpp
+++ b/src/World/Generation/ChunkMeshing.hpp
@@ -19,10 +19,10 @@ class SurroundingContext {
 public:
     struct Block { Bool does_exist; Chunk::BlockData block; };
 
-    Block& at(Position::BlockOffset p);
-    const Block& at(Position::BlockOffset p) const;
+    Block& at(Position::BlockLocalOffset p);
+    const Block& at(Position::BlockLocalOffset p) const;
 private:
-    static USize pos(Position::BlockOffset p);
+    static USize pos(Position::BlockLocalOffset p);
     static constexpr USize surrounding_block_count = Chunk::Width * 4 + 4;
 
     Block m_blocks[surrounding_block_count * Chunk::Height] = {};
@@ -83,7 +83,7 @@ public:
     static Face<AO> face_ao_values(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side);
 
     static Vector<3, I32> get_face_normal(BlockSide side);
-    static SurroundingContext::Block get_block_from_chunk_or_context(const Chunk& chunk, const SurroundingContext& context, Position::BlockOffset pos);
+    static SurroundingContext::Block get_block_from_chunk_or_context(const Chunk& chunk, const SurroundingContext& context, Position::BlockLocalOffset pos);
     static SurroundingContext::Block get_opposing_neighbor(const Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side);
 
     static Bool is_face_visible(Chunk& chunk, const SurroundingContext& context, U32 x, U32 y, U32 z, BlockSide side);
diff --git a/src/World/Position.hpp b/src/World/Position.hpp
index 07de6f3..bc874c0 100644
--- a/src/World/Position.hpp
+++ b/src/World/Position.hpp
@@ -2,38 +2,36 @@
 
 #include "array"
 #include "ChunkDimensions.hpp"
+#include "../Math/Mod.hpp"
+#include "../Math/Random.hpp"
 #include "../Math/Vector.hpp"
 
 namespace MC::Position {
 
-// Position within entire world.
-using World = Vector<3>;
-
-// Offset between two world positions.
-using WorldOffset = Vector<3>;
+#define MC_POSITION_MAKE_DEFAULT_CONSTRUCTORS(name, t)                          \
+    name() = default;                                                           \
+    template<typename ...Args, std::enable_if_t<sizeof...(Args) == 3, Int> = 0> \
+    name(Args... args) : Vector{ static_cast<t>(args)... } {}                   \
+    name(Vector v) : Vector(v) {}
 
 // Offset between block positions within single chunk.
-class BlockOffset : public Vector<3, I16> {
+class BlockLocalOffset : public Vector<3, I16> {
 public:
-    BlockOffset(I16 x, I16 y, I16 z) : Vector(x, y, z) {}
+    MC_POSITION_MAKE_DEFAULT_CONSTRUCTORS(BlockLocalOffset, I16)
+
     Bool fits_within_chunk() const {
         using namespace MC::World::ChunkDimensions;
         return x() >= 0 && x() < Width && y() >= 0 && y() < Height && z() >= 0 && z() < Width;
     }
 };
 
-// Position of a block within entire world.
-class BlockWorld : public Vector<3, I64> {
-public:
-    BlockWorld(I64 x, I64 y, I64 z) : Vector(x, y, z) {}
-};
-
 // Position of a block within single chunk.
 class BlockLocal : public Vector<3, U8> {
 public:
-    BlockLocal(U8 x, U8 y, U8 z) : Vector(x, y, z) {}
-    BlockLocal(BlockOffset offset) : BlockLocal(offset.x(), offset.y(), offset.z()) {}
-    BlockOffset offset(BlockOffset by) {
+    MC_POSITION_MAKE_DEFAULT_CONSTRUCTORS(BlockLocal, U8)
+    BlockLocal(BlockLocalOffset offset) : BlockLocal(offset.x(), offset.y(), offset.z()) {}
+
+    BlockLocalOffset offset(BlockLocalOffset by) {
         return {
             static_cast<I8>(x() + by.x()),
             static_cast<I8>(y() + by.y()),
@@ -42,8 +40,42 @@ public:
     }
 };
 
-const std::array<BlockOffset, 6> axis_directions = {{
+// Offset between block positions within entire world.
+class BlockWorldOffset : public Vector<3, I64> {
+public:
+    MC_POSITION_MAKE_DEFAULT_CONSTRUCTORS(BlockWorldOffset, I64)
+};
+
+// Position of a block within entire world.
+class BlockWorld : public Vector<3, I64> {
+public:
+    MC_POSITION_MAKE_DEFAULT_CONSTRUCTORS(BlockWorld, I64)
+
+    BlockLocal to_local() const {
+        using namespace MC::World::ChunkDimensions;
+        return {Math::mod(x(), Width), y(), Math::mod(z(), Width)};
+    }
+};
+
+const std::array<BlockLocalOffset, 6> axis_directions = {{
     {1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1},
 }};
 
+// Offset between two world positions.
+class WorldOffset : public Vector<3> {
+public:
+    MC_POSITION_MAKE_DEFAULT_CONSTRUCTORS(WorldOffset, Real)
+};
+
+// Position within entire world.
+class World : public Vector<3> {
+public:
+    MC_POSITION_MAKE_DEFAULT_CONSTRUCTORS(World, Real)
+
+    BlockWorld round_to_block() const {
+        auto rounded = map([](auto x) { return std::round(x); });
+        return {rounded.x(), rounded.y(), rounded.z()};
+    }
+};
+
 }
diff --git a/src/World/World.cpp b/src/World/World.cpp
index 3ff0cf4..089c498 100644
--- a/src/World/World.cpp
+++ b/src/World/World.cpp
@@ -42,8 +42,7 @@ std::vector<ChunkRegistry::Data*> World::get_visible_chunks(Position::World posi
 }
 
 std::vector<ChunkIndex> World::get_visible_chunk_indices(const Position::World position) const {
-    I32 center_x = std::round(position.x() / Chunk::Width);
-    I32 center_y = std::round(position.z() / Chunk::Width);
+    ChunkIndex center = ChunkIndex::from_position(position.round_to_block());
 
     std::vector<ChunkIndex> indices{};
     indices.reserve(m_view_distance_radius * m_view_distance_radius * 4);
@@ -52,8 +51,8 @@ std::vector<ChunkIndex> World::get_visible_chunk_indices(const Position::World p
         I32 height = std::round(std::sqrt(radius * radius - x * x) + 0.5);
         for (I32 y = -height; y <= height; y++) {
             // Needed so that this function and is_chunk_in_radius forcibly agree.
-            if (!is_chunk_in_radius(position, {x + center_x, y + center_y})) continue;
-            indices.emplace_back(x + center_x, y + center_y);
+            if (!is_chunk_in_radius(position, {x + center.x, y + center.y})) continue;
+            indices.emplace_back(x + center.x, y + center.y);
         }
     }