summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/Common/Iteration.hpp6
-rw-r--r--src/Entities/Player.cpp3
-rw-r--r--src/Math/Vector.hpp6
-rw-r--r--src/World/Chunk.cpp10
-rw-r--r--src/World/Chunk.hpp30
-rw-r--r--src/World/Generation/ChunkMeshing.hpp41
-rw-r--r--src/World/Generation/Decoration.cpp68
8 files changed, 99 insertions, 66 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a4cf912..a418b7c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -85,6 +85,7 @@ add_executable(meowcraft
     src/World/VoxelTraversal.hpp
     src/Common/Assert.hpp
     src/Common/Pure.hpp
+    src/Common/Iteration.hpp
     src/GFX/Actions.hpp
     src/GFX/Resources.cpp src/GFX/Resources.hpp
     src/ThreadRole.hpp
diff --git a/src/Common/Iteration.hpp b/src/Common/Iteration.hpp
new file mode 100644
index 0000000..cdcecd2
--- /dev/null
+++ b/src/Common/Iteration.hpp
@@ -0,0 +1,6 @@
+#pragma once
+
+enum class Iteration {
+    Continue,
+    Break,
+};
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 83c3f5e..8dc94a1 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -282,9 +282,10 @@ std::vector<AABB> Player::terrain_collision_domain(
         if (!chunk.chunk.has_value()) continue;
         auto chunk_position = chunk.chunk.value().position();
         chunk.chunk->for_each([&](auto p, auto b) {
-            if (!b.type.is_solid()) return;
+            if (!b.type.is_solid()) return Iteration::Continue;
             auto block_bounds = World::Chunk::block_bounds(p).offset(chunk_position);
             if (domain_box.collides(block_bounds)) colliding_blocks.push_back(block_bounds);
+            return Iteration::Continue;
         });
     }
 
diff --git a/src/Math/Vector.hpp b/src/Math/Vector.hpp
index 3525e42..d179049 100644
--- a/src/Math/Vector.hpp
+++ b/src/Math/Vector.hpp
@@ -204,6 +204,12 @@ struct Vector {
     T& w() { static_assert(S > 3); return elements[3]; }
     const T& w() const { static_assert(S > 3); return elements[3]; }
 
+    std::array<T, S> values() const {
+        std::array<T, S> result{};
+        for (UInt i = 0; i < S; i++) result[i] = elements[i];
+        return result;
+    }
+
     std::string string() const {
         std::stringstream str{};
 
diff --git a/src/World/Chunk.cpp b/src/World/Chunk.cpp
index b4a9ece..eeaefa0 100644
--- a/src/World/Chunk.cpp
+++ b/src/World/Chunk.cpp
@@ -39,10 +39,18 @@ Bool Chunk::is_valid_position(Position::BlockLocal pos) {
     return pos.x() < Width && pos.y() < Height && pos.z() < Width;
 }
 
-U64 Chunk::pos(U32 x, U32 y, U32 z) {
+U64 Chunk::pos(U32 const x, U32 const y, U32 const z) {
     return x + Width * y + Width * Height * z;
 }
 
+Position::BlockLocal Chunk::pos(U64 const i) {
+    return {
+        i % Width,
+        i / Width % Height,
+        i / (Width * Height)
+    };
+}
+
 AABB Chunk::block_bounds(Position::BlockLocal pos) {
     return {
         {pos.x(), pos.y(), pos.z()},
diff --git a/src/World/Chunk.hpp b/src/World/Chunk.hpp
index bab20f2..e958b9a 100644
--- a/src/World/Chunk.hpp
+++ b/src/World/Chunk.hpp
@@ -7,6 +7,7 @@
 #include "BlockType.hpp"
 #include "ChunkIndex.hpp"
 #include "Position.hpp"
+#include "../Common/Iteration.hpp"
 #include "../GFX/Mesh.hpp"
 
 namespace MC::World {
@@ -16,6 +17,8 @@ public:
     static constexpr U32 Width = ChunkDimensions::Width;
     static constexpr U32 Height = ChunkDimensions::Height;
 
+    static constexpr U32 BlockCount = Width * Height * Width;
+
     Chunk(I64 x, I64 y)
         : m_blocks{Width * Height * Width, {BlockType::Air}},
         m_index(x, y),
@@ -48,16 +51,32 @@ public:
     void set_details(const Details& details) { m_details = details; }
     Details& details(){ return m_details; }
 
+    // Cache-friendly iteration through all blocks in the chunk.
     template <typename F>
     void for_each(F f) {
+        for (U32 i = 0; i < BlockCount; ++i) {
+            Iteration control = f(pos(i), m_blocks[i]);
+            if (control == Iteration::Break) break;
+        }
+    }
+
+    // Iteration through all blocks in the chunk by column.
+    // Starts from the top of the chunk and goes down.
+    enum class ColumnIteration { Continue, Break, SkipColumn };
+    template <typename F>
+    void for_each_by_column(F f) {
+        // TODO: Maybe add a way to lookup the highest block in a column
+        // to skip all the air?
         for (U32 x = 0; x < Width; ++x) {
-            for (U32 y = 0; y < Height; ++y) {
-                for (U32 z = 0; z < Width; ++z) {
-                    Position::BlockLocal pos{x, y, z};
-                    f(pos, at(x, y, z));
+            for (U32 z = 0; z < Width; ++z) {
+                for (UInt y = Height - 1; y != 0; y--) {
+                    ColumnIteration control = f({x, y, z}, m_blocks.at(pos(x, y, z)));
+                    if (control == ColumnIteration::Break) goto end;
+                    if (control == ColumnIteration::SkipColumn) break;
                 }
             }
         }
+        end: return;
     }
 
     ChunkIndex index() const;
@@ -70,7 +89,10 @@ public:
     static AABB block_bounds(Position::BlockLocal pos);
     static AABB block_bounds(Position::BlockWorld pos);
 private:
+    // Convert a local position to a chunk block index.
     static U64 pos(U32 x, U32 y, U32 z);
+    // Convert a chunk block index to a local position.
+    static Position::BlockLocal pos(U64 i);
 
     ChunkIndex m_index;
     Vector<3> m_position;
diff --git a/src/World/Generation/ChunkMeshing.hpp b/src/World/Generation/ChunkMeshing.hpp
index c745a3b..16ba701 100644
--- a/src/World/Generation/ChunkMeshing.hpp
+++ b/src/World/Generation/ChunkMeshing.hpp
@@ -47,29 +47,26 @@ template <typename Decisions>
 GFX::Mesh create_mesh(Chunk& chunk, const SurroundingContext& context) {
     GFX::Util::MeshBuilder<Normal, TexCoord, Light, AO> builder;
 
-    for (Int x = 0; x < Chunk::Width; x++) {
-        for (Int y = 0; y < Chunk::Height; y++) {
-            for (Int z = 0; z < Chunk::Width; z++) {
-                auto block = chunk.at(x, y, z);
-                if (Decisions::should_ignore_block(block))
-                    continue;
-
-                for (auto side: BlockSide::all()) {
-                    if (!Decisions::is_face_visible(chunk, context, x, y, z, side))
-                        continue;
-
-                    U32 s = builder.vertex_count();
-
-                    builder.positions(Decisions::face_positions(side, x, y, z));
-                    builder.attributes<0>(Decisions::face_normals(side));
-                    builder.attributes<1>(Decisions::face_tex_coords(block.type, side));
-                    builder.attributes<2>(Decisions::face_light(chunk, context, x, y, z, side));
-                    builder.attributes<3>(Decisions::face_ao_values(chunk, context, x, y, z, side));
-                    builder.indices(std::array{ s + 0, s + 1, s + 2, s + 2, s + 3, s + 0 });
-                }
-            }
+    chunk.for_each([&](Position::BlockLocal pos, Chunk::BlockData block) {
+        auto [x, y, z] = pos.values();
+        if (Decisions::should_ignore_block(block)) return Iteration::Continue;
+
+        for (auto side: BlockSide::all()) {
+            if (!Decisions::is_face_visible(chunk, context, x, y, z, side))
+                continue;
+
+            builder.positions(Decisions::face_positions(side, x, y, z));
+            builder.attributes<0>(Decisions::face_normals(side));
+            builder.attributes<1>(Decisions::face_tex_coords(block.type, side));
+            builder.attributes<2>(Decisions::face_light(chunk, context, x, y, z, side));
+            builder.attributes<3>(Decisions::face_ao_values(chunk, context, x, y, z, side));
+
+            U32 s = builder.vertex_count();
+            builder.indices(std::array{ s + 0, s + 1, s + 2, s + 2, s + 3, s + 0 });
         }
-    }
+
+        return Iteration::Continue;
+    });
 
     return builder.mesh();
 }
diff --git a/src/World/Generation/Decoration.cpp b/src/World/Generation/Decoration.cpp
index 0623c05..ecfa49b 100644
--- a/src/World/Generation/Decoration.cpp
+++ b/src/World/Generation/Decoration.cpp
@@ -43,35 +43,31 @@ void Decorator::draw_circle(Chunk& chunk, Pos pos, Vector<3> axis, Real radius,
 
 void TreeDecorator::decorate_chunk(Chunk& chunk) {
     Pos last_tree = Pos::max();
-    for (UInt x = 0; x < Chunk::Width; x++) {
-        for (UInt z = 0; z < Chunk::Width; z++) {
-            for (UInt y = Chunk::Height - 1; y != 0; y--) {
-                Pos pos{x, y, z};
-                if (!is_valid_position(pos))
-                    continue;
-
-                auto& block_below = chunk.at(x, y-1, z);
-                if (block_below.empty())
-                    continue;
-
-                auto type = block_below.type;
-                if (type != BlockType::Snow && type != BlockType::Grass && type != BlockType::Dirt)
-                    break;
-
-                auto noise = m_tree_noise.at({(Real)x, (Real)z});
-                if (noise < 0.8f)
-                    continue;
-
-                if (last_tree.distance(pos) < s_tree_radius * 3)
-                    continue;
-
-                draw_tree(chunk, pos);
-                block_below = {BlockType::Dirt};
-                last_tree = pos;
-                break;
-            }
-        }
-    }
+    chunk.for_each_by_column([&](Pos pos, Chunk::BlockData& block) {
+        auto pos_above = pos + Pos::up();
+        if (!is_valid_position(pos_above))
+            return Chunk::ColumnIteration::Continue;
+
+        if (block.empty())
+            return Chunk::ColumnIteration::Continue;
+
+        auto type = block.type;
+        if (type != BlockType::Snow && type != BlockType::Grass && type != BlockType::Dirt)
+            return Chunk::ColumnIteration::SkipColumn;
+
+        auto noise = m_tree_noise.at({TO(Real, pos.x()), TO(Real, pos.z())});
+        if (noise < 0.8f)
+            return Chunk::ColumnIteration::Continue;
+
+        if (last_tree.distance(pos_above) < s_tree_radius * 3)
+            return Chunk::ColumnIteration::Continue;
+
+        draw_tree(chunk, pos_above);
+        block = {BlockType::Dirt};
+        last_tree = pos_above;
+
+        return Chunk::ColumnIteration::SkipColumn;
+    });
 }
 
 void TreeDecorator::draw_tree(Chunk& chunk, Pos pos) const {
@@ -97,15 +93,11 @@ Bool TreeDecorator::is_valid_position(Pos pos) {
 }
 
 void DefaultLightDecorator::decorate_chunk(Chunk& chunk) {
-    for (UInt x = 0; x < Chunk::Width; x++) {
-        for (UInt z = 0; z < Chunk::Width; z++) {
-            for (UInt y = Chunk::Height - 1; y != 0; y--) {
-                auto& block = chunk.at(x, y, z);
-                if (!block.type.is_translucent()) break;
-                chunk.at(x, y, z).light = 200;
-            }
-        }
-    }
+    chunk.for_each_by_column([&](Pos pos, Chunk::BlockData& block) {
+        if (!block.type.is_translucent()) return Chunk::ColumnIteration::Break;
+        block.light = 200;
+        return Chunk::ColumnIteration::Continue;
+    });
 }
 
 }