]> git.localhorst.tv Git - blank.git/blobdiff - src/chunk.cpp
better chunk memory management
[blank.git] / src / chunk.cpp
index 994d68e41bf4e930d4d55df86379381685e4f4c3..a64e184140f503d4d343b26c04207571db0c459e 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "generator.hpp"
 
+#include <algorithm>
 #include <limits>
 #include <queue>
 #include <glm/gtx/transform.hpp>
@@ -11,9 +12,9 @@ namespace blank {
 
 Chunk::Chunk(const BlockTypeRegistry &types)
 : types(&types)
-, neighbor{ 0, 0, 0, 0, 0, 0 }
-, blocks()
-, light()
+, neighbor{0}
+, blocks{}
+, light{0}
 , model()
 , position(0, 0, 0)
 , dirty(false) {
@@ -22,23 +23,19 @@ Chunk::Chunk(const BlockTypeRegistry &types)
 
 Chunk::Chunk(Chunk &&other)
 : types(other.types)
-, blocks(std::move(other.blocks))
-, light(std::move(other.light))
 , model(std::move(other.model))
 , position(other.position)
 , dirty(other.dirty) {
-       for (size_t i = 0; i < Block::FACE_COUNT; ++i) {
-               neighbor[i] = other.neighbor[i];
-       }
+       std::copy(other.neighbor, other.neighbor + sizeof(neighbor), neighbor);
+       std::copy(other.blocks, other.blocks + sizeof(blocks), blocks);
+       std::copy(other.light, other.light + sizeof(light), light);
 }
 
 Chunk &Chunk::operator =(Chunk &&other) {
        types = other.types;
-       for (size_t i = 0; i < Block::FACE_COUNT; ++i) {
-               neighbor[i] = other.neighbor[i];
-       }
-       blocks = std::move(other.blocks);
-       light = std::move(other.light);
+       std::copy(other.neighbor, other.neighbor + sizeof(neighbor), neighbor);
+       std::copy(other.blocks, other.blocks + sizeof(blocks), blocks);
+       std::copy(other.light, other.light + sizeof(light), light);
        model = std::move(other.model);
        position = other.position;
        dirty = other.dirty;
@@ -46,77 +43,6 @@ Chunk &Chunk::operator =(Chunk &&other) {
 }
 
 
-void Chunk::SetNeighbor(Chunk &other) {
-       if (other.position == position + Pos(-1, 0, 0)) {
-               neighbor[Block::FACE_LEFT] = &other;
-               other.neighbor[Block::FACE_RIGHT] = this;
-       } else if (other.position == position + Pos(1, 0, 0)) {
-               neighbor[Block::FACE_RIGHT] = &other;
-               other.neighbor[Block::FACE_LEFT] = this;
-       } else if (other.position == position + Pos(0, -1, 0)) {
-               neighbor[Block::FACE_DOWN] = &other;
-               other.neighbor[Block::FACE_UP] = this;
-       } else if (other.position == position + Pos(0, 1, 0)) {
-               neighbor[Block::FACE_UP] = &other;
-               other.neighbor[Block::FACE_DOWN] = this;
-       } else if (other.position == position + Pos(0, 0, -1)) {
-               neighbor[Block::FACE_BACK] = &other;
-               other.neighbor[Block::FACE_FRONT] = this;
-       } else if (other.position == position + Pos(0, 0, 1)) {
-               neighbor[Block::FACE_FRONT] = &other;
-               other.neighbor[Block::FACE_BACK] = this;
-       }
-}
-
-void Chunk::ClearNeighbors() {
-       for (int i = 0; i < Block::FACE_COUNT; ++i) {
-               neighbor[i] = nullptr;
-       }
-}
-
-void Chunk::Unlink() {
-       if (neighbor[Block::FACE_UP]) {
-               neighbor[Block::FACE_UP]->neighbor[Block::FACE_DOWN] = nullptr;
-       }
-       if (neighbor[Block::FACE_DOWN]) {
-               neighbor[Block::FACE_DOWN]->neighbor[Block::FACE_UP] = nullptr;
-       }
-       if (neighbor[Block::FACE_LEFT]) {
-               neighbor[Block::FACE_LEFT]->neighbor[Block::FACE_RIGHT] = nullptr;
-       }
-       if (neighbor[Block::FACE_RIGHT]) {
-               neighbor[Block::FACE_RIGHT]->neighbor[Block::FACE_LEFT] = nullptr;
-       }
-       if (neighbor[Block::FACE_FRONT]) {
-               neighbor[Block::FACE_FRONT]->neighbor[Block::FACE_BACK] = nullptr;
-       }
-       if (neighbor[Block::FACE_BACK]) {
-               neighbor[Block::FACE_BACK]->neighbor[Block::FACE_FRONT] = nullptr;
-       }
-}
-
-void Chunk::Relink() {
-       if (neighbor[Block::FACE_UP]) {
-               neighbor[Block::FACE_UP]->neighbor[Block::FACE_DOWN] = this;
-       }
-       if (neighbor[Block::FACE_DOWN]) {
-               neighbor[Block::FACE_DOWN]->neighbor[Block::FACE_UP] = this;
-       }
-       if (neighbor[Block::FACE_LEFT]) {
-               neighbor[Block::FACE_LEFT]->neighbor[Block::FACE_RIGHT] = this;
-       }
-       if (neighbor[Block::FACE_RIGHT]) {
-               neighbor[Block::FACE_RIGHT]->neighbor[Block::FACE_LEFT] = this;
-       }
-       if (neighbor[Block::FACE_FRONT]) {
-               neighbor[Block::FACE_FRONT]->neighbor[Block::FACE_BACK] = this;
-       }
-       if (neighbor[Block::FACE_BACK]) {
-               neighbor[Block::FACE_BACK]->neighbor[Block::FACE_FRONT] = this;
-       }
-}
-
-
 namespace {
 
 struct SetNode {
@@ -132,11 +58,11 @@ struct SetNode {
 
        bool HasNext(Block::Face face) {
                const BlockLookup next(chunk, pos, face);
-               return next.result && !next.chunk->Type(*next.result).block_light;
+               return next && !next.GetType().block_light;
        }
        SetNode GetNext(Block::Face face) {
                const BlockLookup next(chunk, pos, face);
-               return SetNode(next.chunk, next.pos);
+               return SetNode(&next.GetChunk(), next.GetBlockPos());
        }
 
 };
@@ -155,7 +81,7 @@ struct UnsetNode
 
        bool HasNext(Block::Face face) {
                const BlockLookup next(chunk, pos, face);
-               return next.result;
+               return next;
        }
        UnsetNode GetNext(Block::Face face) { return UnsetNode(SetNode::GetNext(face)); }
 
@@ -227,10 +153,12 @@ void Chunk::SetBlock(int index, const Block &block) {
                work_light();
        } else if (new_type.block_light && !old_type.block_light) {
                // obstacle added
-               dark_queue.emplace(this, ToPos(index));
-               SetLight(index, 0);
-               work_dark();
-               work_light();
+               if (GetLight(index) > 0) {
+                       dark_queue.emplace(this, ToPos(index));
+                       SetLight(index, 0);
+                       work_dark();
+                       work_light();
+               }
        } else if (!new_type.block_light && old_type.block_light) {
                // obstacle removed
                int level = 0;
@@ -268,6 +196,166 @@ const Block *Chunk::FindNext(const Pos &pos, Block::Face face) const {
        }
 }
 
+void Chunk::SetNeighbor(Chunk &other) {
+       if (other.position == position + Pos(-1, 0, 0)) {
+               if (neighbor[Block::FACE_LEFT] != &other) {
+                       neighbor[Block::FACE_LEFT] = &other;
+                       other.neighbor[Block::FACE_RIGHT] = this;
+                       for (int z = 0; z < Depth(); ++z) {
+                               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);
+                                       }
+                               }
+                       }
+                       work_light();
+               }
+       } else if (other.position == position + Pos(1, 0, 0)) {
+               if (neighbor[Block::FACE_RIGHT] != &other) {
+                       neighbor[Block::FACE_RIGHT] = &other;
+                       other.neighbor[Block::FACE_LEFT] = this;
+                       for (int z = 0; z < Depth(); ++z) {
+                               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);
+                                       }
+                               }
+                       }
+                       work_light();
+               }
+       } else if (other.position == position + Pos(0, -1, 0)) {
+               if (neighbor[Block::FACE_DOWN] != &other) {
+                       neighbor[Block::FACE_DOWN] = &other;
+                       other.neighbor[Block::FACE_UP] = this;
+                       for (int z = 0; z < Depth(); ++z) {
+                               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);
+                                       }
+                               }
+                       }
+                       work_light();
+               }
+       } else if (other.position == position + Pos(0, 1, 0)) {
+               if (neighbor[Block::FACE_UP] != &other) {
+                       neighbor[Block::FACE_UP] = &other;
+                       other.neighbor[Block::FACE_DOWN] = this;
+                       for (int z = 0; z < Depth(); ++z) {
+                               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);
+                                       }
+                               }
+                       }
+                       work_light();
+               }
+       } else if (other.position == position + Pos(0, 0, -1)) {
+               if (neighbor[Block::FACE_BACK] != &other) {
+                       neighbor[Block::FACE_BACK] = &other;
+                       other.neighbor[Block::FACE_FRONT] = this;
+                       for (int y = 0; y < Height(); ++y) {
+                               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);
+                                       }
+                               }
+                       }
+                       work_light();
+               }
+       } else if (other.position == position + Pos(0, 0, 1)) {
+               if (neighbor[Block::FACE_FRONT] != &other) {
+                       neighbor[Block::FACE_FRONT] = &other;
+                       other.neighbor[Block::FACE_BACK] = this;
+                       for (int y = 0; y < Height(); ++y) {
+                               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);
+                                       }
+                               }
+                       }
+                       work_light();
+               }
+       }
+}
+
+void Chunk::ClearNeighbors() {
+       for (int i = 0; i < Block::FACE_COUNT; ++i) {
+               neighbor[i] = nullptr;
+       }
+}
+
+void Chunk::Unlink() {
+       if (neighbor[Block::FACE_UP]) {
+               neighbor[Block::FACE_UP]->neighbor[Block::FACE_DOWN] = nullptr;
+       }
+       if (neighbor[Block::FACE_DOWN]) {
+               neighbor[Block::FACE_DOWN]->neighbor[Block::FACE_UP] = nullptr;
+       }
+       if (neighbor[Block::FACE_LEFT]) {
+               neighbor[Block::FACE_LEFT]->neighbor[Block::FACE_RIGHT] = nullptr;
+       }
+       if (neighbor[Block::FACE_RIGHT]) {
+               neighbor[Block::FACE_RIGHT]->neighbor[Block::FACE_LEFT] = nullptr;
+       }
+       if (neighbor[Block::FACE_FRONT]) {
+               neighbor[Block::FACE_FRONT]->neighbor[Block::FACE_BACK] = nullptr;
+       }
+       if (neighbor[Block::FACE_BACK]) {
+               neighbor[Block::FACE_BACK]->neighbor[Block::FACE_FRONT] = nullptr;
+       }
+}
+
+void Chunk::Relink() {
+       if (neighbor[Block::FACE_UP]) {
+               neighbor[Block::FACE_UP]->neighbor[Block::FACE_DOWN] = this;
+       }
+       if (neighbor[Block::FACE_DOWN]) {
+               neighbor[Block::FACE_DOWN]->neighbor[Block::FACE_UP] = this;
+       }
+       if (neighbor[Block::FACE_LEFT]) {
+               neighbor[Block::FACE_LEFT]->neighbor[Block::FACE_RIGHT] = this;
+       }
+       if (neighbor[Block::FACE_RIGHT]) {
+               neighbor[Block::FACE_RIGHT]->neighbor[Block::FACE_LEFT] = this;
+       }
+       if (neighbor[Block::FACE_FRONT]) {
+               neighbor[Block::FACE_FRONT]->neighbor[Block::FACE_BACK] = this;
+       }
+       if (neighbor[Block::FACE_BACK]) {
+               neighbor[Block::FACE_BACK]->neighbor[Block::FACE_FRONT] = this;
+       }
+}
+
 
 void Chunk::SetLight(int index, int level) {
        if (light[index] != level) {
@@ -285,19 +373,10 @@ float Chunk::GetVertexLight(int index, const BlockModel::Position &vtx, const Bl
        Chunk::Pos pos(ToPos(index));
 
        Block::Face direct_face(Block::NormalFace(norm));
-       const Chunk *direct_chunk = this;
-       Chunk::Pos direct_pos(pos + Block::FaceNormal(direct_face));
-       if (!InBounds(direct_pos)) {
-               if (HasNeighbor(direct_face)) {
-                       direct_chunk = &GetNeighbor(direct_face);
-                       direct_pos -= (Block::FaceNormal(direct_face) * Extent());
-                       float direct_light = direct_chunk->GetLight(direct_pos);
-                       if (direct_light > light) {
-                               light = direct_light;
-                       }
-               }
-       } else {
-               float direct_light = direct_chunk->GetLight(direct_pos);
+       // tis okay
+       BlockLookup direct(const_cast<Chunk *>(this), pos, Block::NormalFace(norm));
+       if (direct) {
+               float direct_light = direct.GetLight();
                if (direct_light > light) {
                        light = direct_light;
                }
@@ -331,12 +410,6 @@ bool Chunk::IsSurface(const Pos &pos) const {
 }
 
 
-void Chunk::Allocate() {
-       blocks.resize(Size(), Block(0));
-       light.resize(Size(), 0);
-}
-
-
 void Chunk::Draw() {
        if (dirty) {
                Update();
@@ -529,12 +602,13 @@ glm::mat4 Chunk::ToTransform(int idx) const {
 
 
 BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p)
-: chunk(c), pos(p), result(nullptr) {
+: chunk(c), pos(p) {
        while (pos.x >= Chunk::Width()) {
                if (chunk->HasNeighbor(Block::FACE_RIGHT)) {
                        chunk = &chunk->GetNeighbor(Block::FACE_RIGHT);
                        pos.x -= Chunk::Width();
                } else {
+                       chunk = nullptr;
                        return;
                }
        }
@@ -543,6 +617,7 @@ BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p)
                        chunk = &chunk->GetNeighbor(Block::FACE_LEFT);
                        pos.x += Chunk::Width();
                } else {
+                       chunk = nullptr;
                        return;
                }
        }
@@ -551,6 +626,7 @@ BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p)
                        chunk = &chunk->GetNeighbor(Block::FACE_UP);
                        pos.y -= Chunk::Height();
                } else {
+                       chunk = nullptr;
                        return;
                }
        }
@@ -559,6 +635,7 @@ BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p)
                        chunk = &chunk->GetNeighbor(Block::FACE_DOWN);
                        pos.y += Chunk::Height();
                } else {
+                       chunk = nullptr;
                        return;
                }
        }
@@ -567,6 +644,7 @@ BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p)
                        chunk = &chunk->GetNeighbor(Block::FACE_FRONT);
                        pos.z -= Chunk::Depth();
                } else {
+                       chunk = nullptr;
                        return;
                }
        }
@@ -575,23 +653,18 @@ BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p)
                        chunk = &chunk->GetNeighbor(Block::FACE_BACK);
                        pos.z += Chunk::Depth();
                } else {
+                       chunk = nullptr;
                        return;
                }
        }
-       result = &chunk->BlockAt(pos);
 }
 
 BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p, Block::Face face)
-: chunk(c), pos(p), result(nullptr) {
+: chunk(c), pos(p) {
        pos += Block::FaceNormal(face);
-       if (Chunk::InBounds(pos)) {
-               result = &chunk->BlockAt(pos);
-       } else {
+       if (!Chunk::InBounds(pos)) {
                pos -= Block::FaceNormal(face) * Chunk::Extent();
-               if (chunk->HasNeighbor(face)) {
-                       chunk = &chunk->GetNeighbor(face);
-                       result = &chunk->BlockAt(pos);
-               }
+               chunk = &chunk->GetNeighbor(face);
        }
 }
 
@@ -696,8 +769,8 @@ Chunk &ChunkLoader::Generate(const Chunk::Pos &pos) {
        loaded.emplace_back(reg);
        Chunk &chunk = loaded.back();
        chunk.Position(pos);
-       Insert(chunk);
        gen(chunk);
+       Insert(chunk);
        return chunk;
 }
 
@@ -789,38 +862,31 @@ void ChunkLoader::GenerateSurrounding(const Chunk::Pos &pos) {
 }
 
 void ChunkLoader::Update() {
-       bool reused = false;
-       if (!to_generate.empty()) {
-               Chunk::Pos pos(to_generate.front());
-
-               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);
-                               reused = true;
-                               break;
-                       }
-               }
+       if (to_generate.empty()) {
+               return;
+       }
 
-               if (!reused) {
-                       if (to_free.empty()) {
-                               loaded.emplace_back(reg);
-                       } else {
-                               to_free.front().ClearNeighbors();
-                               loaded.splice(loaded.end(), to_free, to_free.begin());
-                               reused = true;
-                       }
-                       Chunk &chunk = loaded.back();
-                       chunk.Position(pos);
-                       Insert(chunk);
-                       gen(chunk);
+       Chunk::Pos pos(to_generate.front());
+       to_generate.pop_front();
+
+       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);
+                       return;
                }
-               to_generate.pop_front();
        }
 
-       if (!reused && !to_free.empty()) {
-               to_free.pop_front();
+       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);
+       Insert(chunk);
 }
 
 }