]> git.localhorst.tv Git - blank.git/blobdiff - src/world/chunk.cpp
made chunk neighbor linkage a little safer
[blank.git] / src / world / chunk.cpp
index 50595f4856526220a44eb069c4ab9b6374a0d2b9..ef3c5fc869d80acb94d4ec054462ce2d85c9cc64 100644 (file)
@@ -3,8 +3,10 @@
 #include "ChunkLoader.hpp"
 
 #include "Generator.hpp"
+#include "WorldCollision.hpp"
 
 #include <algorithm>
+#include <iostream>
 #include <limits>
 #include <queue>
 
@@ -169,20 +171,41 @@ 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));
                for (int face = 0; face < Block::FACE_COUNT; ++face) {
-                       BlockLookup next_block(this, ToPos(index), Block::Face(face));
+                       BlockLookup next_block(this, pos, Block::Face(face));
                        if (next_block) {
-                               level = std::min(level, next_block.GetLight());
+                               level = std::max(level, next_block.GetLight());
                        }
                }
                if (level > 1) {
                        SetLight(index, level - 1);
-                       light_queue.emplace(this, ToPos(index));
+                       light_queue.emplace(this, pos);
                        work_light();
                }
        }
 }
 
+namespace {
+
+// propagate light from a's edge to b
+void edge_light(
+       Chunk &a, const Chunk::Pos &a_pos,
+       Chunk &b, const Chunk::Pos &b_pos
+) noexcept {
+       if (a.GetLight(a_pos) > 1) {
+               const BlockType &b_type = b.Type(Chunk::ToIndex(b_pos));
+               if (!b_type.block_light) {
+                       light_queue.emplace(&a, a_pos);
+               }
+               if (b_type.visible) {
+                       b.Invalidate();
+               }
+       }
+}
+
+}
+
 void Chunk::SetNeighbor(Chunk &other) noexcept {
        if (other.position == position + Pos(-1, 0, 0)) {
                if (neighbor[Block::FACE_LEFT] != &other) {
@@ -192,12 +215,8 @@ void Chunk::SetNeighbor(Chunk &other) noexcept {
                                for (int y = 0; y < height; ++y) {
                                        Pos my_pos(0, y, z);
                                        Pos other_pos(width - 1, y, z);
-                                       if (GetLight(my_pos) > 0) {
-                                               light_queue.emplace(this, my_pos);
-                                       }
-                                       if (other.GetLight(other_pos) > 0) {
-                                               light_queue.emplace(&other, other_pos);
-                                       }
+                                       edge_light(*this, my_pos, other, other_pos);
+                                       edge_light(other, other_pos, *this, my_pos);
                                }
                        }
                        work_light();
@@ -210,12 +229,8 @@ void Chunk::SetNeighbor(Chunk &other) noexcept {
                                for (int y = 0; y < height; ++y) {
                                        Pos my_pos(width - 1, y, z);
                                        Pos other_pos(0, y, z);
-                                       if (GetLight(my_pos) > 0) {
-                                               light_queue.emplace(this, my_pos);
-                                       }
-                                       if (other.GetLight(other_pos) > 0) {
-                                               light_queue.emplace(&other, other_pos);
-                                       }
+                                       edge_light(*this, my_pos, other, other_pos);
+                                       edge_light(other, other_pos, *this, my_pos);
                                }
                        }
                        work_light();
@@ -228,12 +243,8 @@ void Chunk::SetNeighbor(Chunk &other) noexcept {
                                for (int x = 0; x < width; ++x) {
                                        Pos my_pos(x, 0, z);
                                        Pos other_pos(x, height - 1, z);
-                                       if (GetLight(my_pos) > 0) {
-                                               light_queue.emplace(this, my_pos);
-                                       }
-                                       if (other.GetLight(other_pos) > 0) {
-                                               light_queue.emplace(&other, other_pos);
-                                       }
+                                       edge_light(*this, my_pos, other, other_pos);
+                                       edge_light(other, other_pos, *this, my_pos);
                                }
                        }
                        work_light();
@@ -246,12 +257,8 @@ void Chunk::SetNeighbor(Chunk &other) noexcept {
                                for (int x = 0; x < width; ++x) {
                                        Pos my_pos(x, height - 1, z);
                                        Pos other_pos(x, 0, z);
-                                       if (GetLight(my_pos) > 0) {
-                                               light_queue.emplace(this, my_pos);
-                                       }
-                                       if (other.GetLight(other_pos) > 0) {
-                                               light_queue.emplace(&other, other_pos);
-                                       }
+                                       edge_light(*this, my_pos, other, other_pos);
+                                       edge_light(other, other_pos, *this, my_pos);
                                }
                        }
                        work_light();
@@ -264,12 +271,8 @@ void Chunk::SetNeighbor(Chunk &other) noexcept {
                                for (int x = 0; x < width; ++x) {
                                        Pos my_pos(x, y, 0);
                                        Pos other_pos(x, y, depth - 1);
-                                       if (GetLight(my_pos) > 0) {
-                                               light_queue.emplace(this, my_pos);
-                                       }
-                                       if (other.GetLight(other_pos) > 0) {
-                                               light_queue.emplace(&other, other_pos);
-                                       }
+                                       edge_light(*this, my_pos, other, other_pos);
+                                       edge_light(other, other_pos, *this, my_pos);
                                }
                        }
                        work_light();
@@ -282,12 +285,8 @@ void Chunk::SetNeighbor(Chunk &other) noexcept {
                                for (int x = 0; x < width; ++x) {
                                        Pos my_pos(x, y, depth - 1);
                                        Pos other_pos(x, y, 0);
-                                       if (GetLight(my_pos) > 0) {
-                                               light_queue.emplace(this, my_pos);
-                                       }
-                                       if (other.GetLight(other_pos) > 0) {
-                                               light_queue.emplace(&other, other_pos);
-                                       }
+                                       edge_light(*this, my_pos, other, other_pos);
+                                       edge_light(other, other_pos, *this, my_pos);
                                }
                        }
                        work_light();
@@ -296,23 +295,10 @@ void Chunk::SetNeighbor(Chunk &other) noexcept {
 }
 
 void Chunk::ClearNeighbors() noexcept {
-       for (int i = 0; i < Block::FACE_COUNT; ++i) {
-               neighbor[i] = nullptr;
-       }
-}
-
-void Chunk::Unlink() noexcept {
        for (int face = 0; face < Block::FACE_COUNT; ++face) {
                if (neighbor[face]) {
                        neighbor[face]->neighbor[Block::Opposite(Block::Face(face))] = nullptr;
-               }
-       }
-}
-
-void Chunk::Relink() noexcept {
-       for (int face = 0; face < Block::FACE_COUNT; ++face) {
-               if (neighbor[face]) {
-                       neighbor[face]->neighbor[Block::Opposite(Block::Face(face))] = this;
+                       neighbor[face] = nullptr;
                }
        }
 }
@@ -449,7 +435,6 @@ bool Chunk::Intersection(
        float &dist,
        glm::vec3 &normal
 ) const noexcept {
-       // TODO: should be possible to heavily optimize this
        int idx = 0;
        blkid = -1;
        dist = std::numeric_limits<float>::infinity();
@@ -481,6 +466,36 @@ bool Chunk::Intersection(
        }
 }
 
+bool Chunk::Intersection(
+       const AABB &box,
+       const glm::mat4 &Mbox,
+       const glm::mat4 &Mchunk,
+       std::vector<WorldCollision> &col
+) const noexcept {
+       bool any = false;
+       float penetration;
+       glm::vec3 normal;
+
+       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) {
+                               const BlockType &type = Type(idx);
+                               if (!type.collision) {
+                                       continue;
+                               }
+                               if (type.shape->Intersects(Mchunk * ToTransform(Pos(x, y, z), idx), box, Mbox, penetration, normal)) {
+                                       col.emplace_back(this, idx, penetration, normal);
+                                       any = true;
+                               }
+                       }
+               }
+       }
+       return any;
+}
+
 
 namespace {
 
@@ -627,9 +642,10 @@ ChunkLoader::ChunkLoader(const Config &config, const BlockTypeRegistry &reg, con
 , loaded()
 , to_generate()
 , to_free()
+, gen_timer(config.gen_limit)
 , load_dist(config.load_dist)
 , unload_dist(config.unload_dist) {
-
+       gen_timer.Start();
 }
 
 namespace {
@@ -731,8 +747,15 @@ void ChunkLoader::Insert(Chunk &chunk) noexcept {
        }
 }
 
-void ChunkLoader::Remove(Chunk &chunk) noexcept {
-       chunk.Unlink();
+std::list<Chunk>::iterator ChunkLoader::Remove(std::list<Chunk>::iterator chunk) noexcept {
+       // fetch next entry while chunk's still in the list
+       std::list<Chunk>::iterator next = chunk;
+       ++next;
+       // unlink neighbors so they won't reference a dead chunk
+       chunk->ClearNeighbors();
+       // and move it from loaded to free list
+       to_free.splice(to_free.end(), loaded, chunk);
+       return next;
 }
 
 Chunk *ChunkLoader::Loaded(const Chunk::Pos &pos) noexcept {
@@ -750,7 +773,7 @@ bool ChunkLoader::Queued(const Chunk::Pos &pos) noexcept {
                        return true;
                }
        }
-       return nullptr;
+       return false;
 }
 
 bool ChunkLoader::Known(const Chunk::Pos &pos) noexcept {
@@ -774,6 +797,12 @@ Chunk &ChunkLoader::ForceLoad(const Chunk::Pos &pos) {
        return Generate(pos);
 }
 
+bool ChunkLoader::OutOfRange(const Chunk::Pos &pos) const noexcept {
+       return std::abs(base.x - pos.x) > unload_dist
+                       || std::abs(base.y - pos.y) > unload_dist
+                       || std::abs(base.z - pos.z) > unload_dist;
+}
+
 void ChunkLoader::Rebase(const Chunk::Pos &new_base) {
        if (new_base == base) {
                return;
@@ -782,22 +811,15 @@ void ChunkLoader::Rebase(const Chunk::Pos &new_base) {
 
        // unload far away chunks
        for (auto iter(loaded.begin()), end(loaded.end()); iter != end;) {
-               if (std::abs(base.x - iter->Position().x) > unload_dist
-                               || std::abs(base.y - iter->Position().y) > unload_dist
-                               || std::abs(base.z - iter->Position().z) > unload_dist) {
-                       auto saved = iter;
-                       Remove(*saved);
-                       ++iter;
-                       to_free.splice(to_free.end(), loaded, saved);
+               if (OutOfRange(*iter)) {
+                       iter = Remove(iter);
                } else {
                        ++iter;
                }
        }
        // abort far away queued chunks
        for (auto iter(to_generate.begin()), end(to_generate.end()); iter != end;) {
-               if (std::abs(base.x - iter->x) > unload_dist
-                               || std::abs(base.y - iter->y) > unload_dist
-                               || std::abs(base.z - iter->z) > unload_dist) {
+               if (OutOfRange(*iter)) {
                        iter = to_generate.erase(iter);
                } else {
                        ++iter;
@@ -812,28 +834,36 @@ void ChunkLoader::GenerateSurrounding(const Chunk::Pos &pos) {
        Generate(pos - offset, pos + offset);
 }
 
-void ChunkLoader::Update() {
-       if (to_generate.empty()) {
+void ChunkLoader::Update(int dt) {
+       // check if a chunk generation is scheduled for this frame
+       // and if there's a chunk waiting to be generated
+       gen_timer.Update(dt);
+       if (!gen_timer.Hit() || to_generate.empty()) {
                return;
        }
 
+       // take position of next chunk in queue
        Chunk::Pos pos(to_generate.front());
        to_generate.pop_front();
 
+       // look if the same chunk was already generated and still lingering
        for (auto iter(to_free.begin()), end(to_free.end()); iter != end; ++iter) {
                if (iter->Position() == pos) {
-                       iter->Relink();
                        loaded.splice(loaded.end(), to_free, iter);
+                       Insert(loaded.back());
                        return;
                }
        }
 
+       // if the free list is empty, allocate a new chunk
+       // otherwise clear an unused one
        if (to_free.empty()) {
                loaded.emplace_back(reg);
        } else {
                to_free.front().ClearNeighbors();
                loaded.splice(loaded.end(), to_free, to_free.begin());
        }
+
        Chunk &chunk = loaded.back();
        chunk.Position(pos);
        gen(chunk);