From 5868f740c492a924cb865644b6201db1632b7376 Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Tue, 17 Mar 2015 18:49:21 +0100 Subject: [PATCH] made chunks aware of their neighbors sort of --- src/chunk.cpp | 221 +++++++++++++++++++++++++++++++++++++++++++------- src/chunk.hpp | 35 +++++++- 2 files changed, 222 insertions(+), 34 deletions(-) diff --git a/src/chunk.cpp b/src/chunk.cpp index ae13aec..ab89ef5 100644 --- a/src/chunk.cpp +++ b/src/chunk.cpp @@ -16,6 +16,7 @@ namespace blank { Chunk::Chunk(const BlockTypeRegistry &types) : types(&types) +, neighbor{ 0, 0, 0, 0, 0, 0 } , blocks() , model() , position(0, 0, 0) @@ -28,11 +29,16 @@ Chunk::Chunk(Chunk &&other) , blocks(std::move(other.blocks)) , model(std::move(other.model)) , dirty(other.dirty) { - + for (size_t i = 0; i < Block::FACE_COUNT; ++i) { + neighbor[i] = other.neighbor[i]; + } } 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); model = std::move(other.model); dirty = other.dirty; @@ -40,6 +46,77 @@ 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; + } +} + + void Chunk::Allocate() { blocks.resize(Size()); } @@ -118,9 +195,10 @@ void Chunk::Update() { Model::Index vtx_counter = 0; for (size_t i = 0; i < Size(); ++i) { - if (Obstructed(i)) continue; - const BlockType &type = Type(blocks[i]); + + if (!type.visible || Obstructed(i)) continue; + type.FillModel(buf, ToTransform(i), vtx_counter); vtx_counter += type.shape->VertexCount(); } @@ -130,29 +208,91 @@ void Chunk::Update() { } bool Chunk::Obstructed(int idx) const { - if (IsBorder(idx)) return false; - - // not checking neighbor visibility here, so all - // invisible blocks must have their fill set to 6x false - // (the default, so should be okay) - - const Block &right = blocks[idx + 1]; - if (!Type(right).FaceFilled(right, Block::FACE_LEFT)) return false; + Chunk::Pos pos(ToPos(idx)); + + Chunk::Pos left_pos(pos + Chunk::Pos(-1, 0, 0)); + const Block *left_block = nullptr; + if (InBounds(left_pos)) { + left_block = &BlockAt(left_pos); + } else if (HasNeighbor(Block::FACE_LEFT)) { + left_pos += Chunk::Pos(Width(), 0, 0); + left_block = &GetNeighbor(Block::FACE_LEFT).BlockAt(left_pos); + } else { + return false; + } + if (!Type(*left_block).FaceFilled(*left_block, Block::FACE_RIGHT)) { + return false; + } - const Block &left = blocks[idx - 1]; - if (!Type(left).FaceFilled(left, Block::FACE_RIGHT)) return false; + Chunk::Pos right_pos(pos + Chunk::Pos(1, 0, 0)); + const Block *right_block = nullptr; + if (InBounds(right_pos)) { + right_block = &BlockAt(right_pos); + } else if (HasNeighbor(Block::FACE_RIGHT)) { + right_pos += Chunk::Pos(-Width(), 0, 0); + right_block = &GetNeighbor(Block::FACE_RIGHT).BlockAt(right_pos); + } else { + return false; + } + if (!Type(*right_block).FaceFilled(*right_block, Block::FACE_LEFT)) { + return false; + } - const Block &up = blocks[idx + Width()]; - if (!Type(up).FaceFilled(up, Block::FACE_DOWN)) return false; + Chunk::Pos down_pos(pos + Chunk::Pos(0, -1, 0)); + const Block *down_block = nullptr; + if (InBounds(down_pos)) { + down_block = &BlockAt(down_pos); + } else if (HasNeighbor(Block::FACE_DOWN)) { + down_pos += Chunk::Pos(0, Height(), 0); + down_block = &GetNeighbor(Block::FACE_DOWN).BlockAt(down_pos); + } else { + return false; + } + if (!Type(*down_block).FaceFilled(*down_block, Block::FACE_UP)) { + return false; + } - const Block &down = blocks[idx - Width()]; - if (!Type(down).FaceFilled(down, Block::FACE_UP)) return false; + Chunk::Pos up_pos(pos + Chunk::Pos(0, 1, 0)); + const Block *up_block = nullptr; + if (InBounds(up_pos)) { + up_block = &BlockAt(up_pos); + } else if (HasNeighbor(Block::FACE_UP)) { + up_pos += Chunk::Pos(0, -Height(), 0); + up_block = &GetNeighbor(Block::FACE_UP).BlockAt(up_pos); + } else { + return false; + } + if (!Type(*up_block).FaceFilled(*up_block, Block::FACE_DOWN)) { + return false; + } - const Block &front = blocks[idx + Width() * Height()]; - if (!Type(front).FaceFilled(front, Block::FACE_BACK)) return false; + Chunk::Pos back_pos(pos + Chunk::Pos(0, 0, -1)); + const Block *back_block = nullptr; + if (InBounds(back_pos)) { + back_block = &BlockAt(back_pos); + } else if (HasNeighbor(Block::FACE_BACK)) { + back_pos += Chunk::Pos(0, 0, Depth()); + back_block = &GetNeighbor(Block::FACE_BACK).BlockAt(back_pos); + } else { + return false; + } + if (!Type(*back_block).FaceFilled(*back_block, Block::FACE_FRONT)) { + return false; + } - const Block &back = blocks[idx - Width() * Height()]; - if (!Type(back).FaceFilled(back, Block::FACE_FRONT)) return false; + Chunk::Pos front_pos(pos + Chunk::Pos(0, 0, 1)); + const Block *front_block = nullptr; + if (InBounds(front_pos)) { + front_block = &BlockAt(front_pos); + } else if (HasNeighbor(Block::FACE_FRONT)) { + front_pos += Chunk::Pos(0, 0, -Depth()); + front_block = &GetNeighbor(Block::FACE_FRONT).BlockAt(front_pos); + } else { + return false; + } + if (!Type(*front_block).FaceFilled(*front_block, Block::FACE_BACK)) { + return false; + } return true; } @@ -202,10 +342,8 @@ void ChunkLoader::Generate(const Chunk::Pos &from, const Chunk::Pos &to) { Chunk::Pos pos(x, y, z); if (Known(pos)) { continue; - } else if (x == 0 && y == 0 && z == 0) { - loaded.emplace_back(reg); - loaded.back().Position(pos); - gen(loaded.back()); + } else if (pos == base) { + Generate(pos); // orientation testing // for (int i = 0; i < Block::FACE_COUNT; ++i) { @@ -224,6 +362,25 @@ void ChunkLoader::Generate(const Chunk::Pos &from, const Chunk::Pos &to) { to_generate.sort(ChunkLess(base)); } +Chunk &ChunkLoader::Generate(const Chunk::Pos &pos) { + loaded.emplace_back(reg); + Chunk &chunk = loaded.back(); + chunk.Position(pos); + Insert(chunk); + gen(chunk); + return chunk; +} + +void ChunkLoader::Insert(Chunk &chunk) { + for (Chunk &other : loaded) { + chunk.SetNeighbor(other); + } +} + +void ChunkLoader::Remove(Chunk &chunk) { + chunk.Unlink(); +} + Chunk *ChunkLoader::Loaded(const Chunk::Pos &pos) { for (Chunk &chunk : loaded) { if (chunk.Position() == pos) { @@ -260,10 +417,7 @@ Chunk &ChunkLoader::ForceLoad(const Chunk::Pos &pos) { } } - loaded.emplace_back(reg); - loaded.back().Position(pos); - gen(loaded.back()); - return loaded.back(); + return Generate(pos); } void ChunkLoader::Rebase(const Chunk::Pos &new_base) { @@ -278,6 +432,7 @@ void ChunkLoader::Rebase(const Chunk::Pos &new_base) { || 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); } else { @@ -306,6 +461,7 @@ void ChunkLoader::Update() { 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; @@ -316,11 +472,14 @@ void ChunkLoader::Update() { if (to_free.empty()) { loaded.emplace_back(reg); } else { + to_free.front().ClearNeighbors(); loaded.splice(loaded.end(), to_free, to_free.begin()); reused = true; } - loaded.back().Position(pos); - gen(loaded.back()); + Chunk &chunk = loaded.back(); + chunk.Position(pos); + Insert(chunk); + gen(chunk); } to_generate.pop_front(); } diff --git a/src/chunk.hpp b/src/chunk.hpp index ae77365..6c9eeff 100644 --- a/src/chunk.hpp +++ b/src/chunk.hpp @@ -32,14 +32,20 @@ public: static AABB Bounds() { return AABB{ { 0, 0, 0 }, Extent() }; } - static constexpr bool InBounds(const glm::vec3 &pos) { + static constexpr bool InBounds(const Block::Pos &pos) { return pos.x >= 0 && pos.x < Width() && pos.y >= 0 && pos.y < Height() && pos.z >= 0 && pos.z < Depth(); } - static constexpr int ToIndex(const glm::vec3 &pos) { - return int(pos.x) + int(pos.y) * Width() + int(pos.z) * Width() * Height(); + static constexpr bool InBounds(const Chunk::Pos &pos) { + return + pos.x >= 0 && pos.x < Width() && + pos.y >= 0 && pos.y < Height() && + pos.z >= 0 && pos.z < Depth(); + } + static constexpr int ToIndex(const Chunk::Pos &pos) { + return pos.x + pos.y * Width() + pos.z * Width() * Height(); } static constexpr bool InBounds(int idx) { return idx >= 0 && idx < Size(); @@ -51,6 +57,13 @@ public: 0.5f + (idx / (Width() * Height())) ); } + static Chunk::Pos ToPos(int idx) { + return Chunk::Pos( + (idx % Width()), + ((idx / Width()) % Height()), + (idx / (Width() * Height())) + ); + } glm::mat4 ToTransform(int idx) const; static constexpr bool IsBorder(int idx) { @@ -63,6 +76,14 @@ public: (idx / Width()) % Height() == Height() - 1; // high Y plane } + void SetNeighbor(Chunk &); + bool HasNeighbor(Block::Face f) const { return neighbor[f]; } + Chunk &GetNeighbor(Block::Face f) { return *neighbor[f]; } + const Chunk &GetNeighbor(Block::Face f) const { return *neighbor[f]; } + void ClearNeighbors(); + void Unlink(); + void Relink(); + // check if block at given index is completely enclosed (and therefore invisible) bool Obstructed(int idx) const; @@ -73,6 +94,8 @@ public: const Block &BlockAt(int index) const { return blocks[index]; } Block &BlockAt(const Block::Pos &pos) { return BlockAt(ToIndex(pos)); } const Block &BlockAt(const Block::Pos &pos) const { return BlockAt(ToIndex(pos)); } + Block &BlockAt(const Chunk::Pos &pos) { return BlockAt(ToIndex(pos)); } + const Block &BlockAt(const Chunk::Pos &pos) const { return BlockAt(ToIndex(pos)); } const BlockType &Type(const Block &b) const { return *types->Get(b.type); } @@ -103,6 +126,7 @@ private: private: const BlockTypeRegistry *types; + Chunk *neighbor[Block::FACE_COUNT]; std::vector blocks; Model model; Pos position; @@ -130,6 +154,11 @@ public: void Rebase(const Chunk::Pos &); void Update(); +private: + Chunk &Generate(const Chunk::Pos &pos); + void Insert(Chunk &); + void Remove(Chunk &); + private: Chunk::Pos base; -- 2.39.2