X-Git-Url: https://git.localhorst.tv/?a=blobdiff_plain;f=src%2Fchunk.cpp;h=3824a471ac321936bb8c794bc560105cef282525;hb=e53a0e2e711a7d8bd9b0ddacd1360aa14370643f;hp=04c64f26738170a98b64345ddfc9712463ce70d5;hpb=374843f5b3ae60c0d02704a8da5100ac8abe7f1a;p=blank.git diff --git a/src/chunk.cpp b/src/chunk.cpp index 04c64f2..3824a47 100644 --- a/src/chunk.cpp +++ b/src/chunk.cpp @@ -2,44 +2,437 @@ #include "generator.hpp" +#include #include -#include +#include namespace blank { -Chunk::Chunk(const BlockTypeRegistry &types) +constexpr int Chunk::width; +constexpr int Chunk::height; +constexpr int Chunk::depth; +constexpr int Chunk::size; + + +Chunk::Chunk(const BlockTypeRegistry &types) noexcept : types(&types) -, blocks() +, neighbor{0} +, blocks{} +, light{0} , model() , position(0, 0, 0) , dirty(false) { } -Chunk::Chunk(Chunk &&other) +Chunk::Chunk(Chunk &&other) noexcept : types(other.types) -, blocks(std::move(other.blocks)) , model(std::move(other.model)) +, position(other.position) , dirty(other.dirty) { - + 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) { +Chunk &Chunk::operator =(Chunk &&other) noexcept { types = other.types; - blocks = std::move(other.blocks); + 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; return *this; } -void Chunk::Allocate() { - blocks.resize(Size()); +namespace { + +struct SetNode { + + Chunk *chunk; + Chunk::Pos pos; + + SetNode(Chunk *chunk, Chunk::Pos pos) + : chunk(chunk), pos(pos) { } + + int Get() const noexcept { return chunk->GetLight(pos); } + void Set(int level) noexcept { chunk->SetLight(pos, level); } + + bool HasNext(Block::Face face) noexcept { + const BlockLookup next(chunk, pos, face); + return next && !next.GetType().block_light; + } + SetNode GetNext(Block::Face face) noexcept { + const BlockLookup next(chunk, pos, face); + return SetNode(&next.GetChunk(), next.GetBlockPos()); + } + +}; + +struct UnsetNode +: public SetNode { + + int level; + + UnsetNode(Chunk *chunk, Chunk::Pos pos) + : SetNode(chunk, pos), level(Get()) { } + + UnsetNode(const SetNode &set) + : SetNode(set), level(Get()) { } + + + bool HasNext(Block::Face face) noexcept { + const BlockLookup next(chunk, pos, face); + return next; + } + UnsetNode GetNext(Block::Face face) noexcept { return UnsetNode(SetNode::GetNext(face)); } + +}; + +std::queue light_queue; +std::queue dark_queue; + +void work_light() noexcept { + while (!light_queue.empty()) { + SetNode node = light_queue.front(); + light_queue.pop(); + + int level = node.Get() - 1; + for (int face = 0; face < Block::FACE_COUNT; ++face) { + if (node.HasNext(Block::Face(face))) { + SetNode other = node.GetNext(Block::Face(face)); + if (other.Get() < level) { + other.Set(level); + light_queue.emplace(other); + } + } + } + } +} + +void work_dark() noexcept { + while (!dark_queue.empty()) { + UnsetNode node = dark_queue.front(); + dark_queue.pop(); + + for (int face = 0; face < Block::FACE_COUNT; ++face) { + if (node.HasNext(Block::Face(face))) { + UnsetNode other = node.GetNext(Block::Face(face)); + // TODO: if there a light source here with the same level this will err + if (other.Get() != 0 && other.Get() < node.level) { + other.Set(0); + dark_queue.emplace(other); + } else { + light_queue.emplace(other); + } + } + } + } +} + +} + +void Chunk::SetBlock(int index, const Block &block) noexcept { + const BlockType &old_type = Type(blocks[index]); + const BlockType &new_type = Type(block); + + blocks[index] = block; + + if (&old_type == &new_type) return; + + if (new_type.luminosity > old_type.luminosity) { + // light added + SetLight(index, new_type.luminosity); + light_queue.emplace(this, ToPos(index)); + work_light(); + } else if (new_type.luminosity < old_type.luminosity) { + // light removed + dark_queue.emplace(this, ToPos(index)); + SetLight(index, 0); + work_dark(); + SetLight(index, new_type.luminosity); + light_queue.emplace(this, ToPos(index)); + work_light(); + } else if (new_type.block_light && !old_type.block_light) { + // obstacle added + 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; + for (int face = 0; face < Block::FACE_COUNT; ++face) { + BlockLookup next_block(this, ToPos(index), Block::Face(face)); + if (next_block) { + level = std::min(level, next_block.GetLight()); + } + } + if (level > 1) { + SetLight(index, level - 1); + light_queue.emplace(this, ToPos(index)); + work_light(); + } + } +} + +void Chunk::SetNeighbor(Chunk &other) noexcept { + 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() 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; + } + } +} + + +void Chunk::SetLight(int index, int level) noexcept { + if (light[index] != level) { + light[index] = level; + Invalidate(); + } +} + +int Chunk::GetLight(int index) const noexcept { + return light[index]; +} + +float Chunk::GetVertexLight(const Pos &pos, const BlockModel::Position &vtx, const Model::Normal &norm) const noexcept { + int index = ToIndex(pos); + float light = GetLight(index); + + Block::Face direct_face(Block::NormalFace(norm)); + // tis okay + BlockLookup direct(const_cast(this), pos, Block::NormalFace(norm)); + if (direct) { + float direct_light = direct.GetLight(); + if (direct_light > light) { + light = direct_light; + } + } else { + return light; + } + + if (Type(BlockAt(index)).luminosity > 0 || direct.GetType().block_light) { + return light; + } + + Block::Face edge[2]; + switch (Block::Axis(direct_face)) { + case 0: // X + edge[0] = (vtx.y - pos.y) > 0.5f ? Block::FACE_UP : Block::FACE_DOWN; + edge[1] = (vtx.z - pos.z) > 0.5f ? Block::FACE_FRONT : Block::FACE_BACK; + break; + case 1: // Y + edge[0] = (vtx.z - pos.z) > 0.5f ? Block::FACE_FRONT : Block::FACE_BACK; + edge[1] = (vtx.x - pos.x) > 0.5f ? Block::FACE_RIGHT : Block::FACE_LEFT; + break; + case 2: // Z + edge[0] = (vtx.x - pos.x) > 0.5f ? Block::FACE_RIGHT : Block::FACE_LEFT; + edge[1] = (vtx.y - pos.y) > 0.5f ? Block::FACE_UP : Block::FACE_DOWN; + break; + } + + int num = 1; + int occlusion = 0; + + BlockLookup next[2] = { + direct.Next(edge[0]), + direct.Next(edge[1]), + }; + + if (next[0]) { + if (next[0].GetType().block_light) { + ++occlusion; + } else { + light += next[0].GetLight(); + ++num; + } + } + if (next[1]) { + if (next[1].GetType().block_light) { + ++occlusion; + } else { + light += next[1].GetLight(); + ++num; + } + } + if (occlusion < 2) { + if (next[0]) { + BlockLookup corner = next[0].Next(edge[1]); + if (corner) { + if (corner.GetType().block_light) { + ++occlusion; + } else { + light += corner.GetLight(); + ++num; + } + } + } else if (next[1]) { + BlockLookup corner = next[1].Next(edge[0]); + if (corner) { + if (corner.GetType().block_light) { + ++occlusion; + } else { + light += corner.GetLight(); + ++num; + } + } + } + } else { + ++occlusion; + } + + return (light / num) - (occlusion * 0.8f); +} + + +bool Chunk::IsSurface(const Pos &pos) const noexcept { + const Block &block = BlockAt(pos); + if (!Type(block).visible) { + return false; + } + for (int face = 0; face < Block::FACE_COUNT; ++face) { + BlockLookup next = BlockLookup(const_cast(this), pos, Block::Face(face)); + if (!next || !next.GetType().visible) { + return true; + } + } + return false; } -void Chunk::Draw() { +void Chunk::Draw() noexcept { if (dirty) { Update(); } @@ -50,107 +443,190 @@ void Chunk::Draw() { bool Chunk::Intersection( const Ray &ray, const glm::mat4 &M, - int *blkid, - float *dist, - glm::vec3 *normal) const { - { // rough check - if (!blank::Intersection(ray, Bounds(), M)) { - return false; - } - } - - if (!blkid && !dist && !normal) { - return true; - } - + int &blkid, + float &dist, + glm::vec3 &normal +) const noexcept { // TODO: should be possible to heavily optimize this - int id = 0; - int closest_id = -1; - float closest_dist = std::numeric_limits::infinity(); - glm::vec3 closest_normal(0, 1, 0); - for (int z = 0; z < Depth(); ++z) { - for (int y = 0; y < Height(); ++y) { - for (int x = 0; x < Width(); ++x, ++id) { - if (!Type(blocks[id]).visible) { + int idx = 0; + blkid = -1; + dist = std::numeric_limits::infinity(); + for (int 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.visible) { continue; } float cur_dist; glm::vec3 cur_norm; - Block::Pos pos(float(x) + 0.5f, float(y) + 0.5f, float(z) + 0.5f); - if (Type(blocks[id]).shape->Intersects(ray, glm::translate(M, pos), cur_dist, cur_norm)) { - if (cur_dist < closest_dist) { - closest_id = id; - closest_dist = cur_dist; - closest_normal = cur_norm; + if (type.shape->Intersects(ray, M * ToTransform(Pos(x, y, z), idx), cur_dist, cur_norm)) { + if (cur_dist < dist) { + blkid = idx; + dist = cur_dist; + normal = cur_norm; } } } } } - if (closest_id < 0) { + if (blkid < 0) { return false; + } else { + normal = glm::vec3(BlockAt(blkid).Transform() * glm::vec4(normal, 0.0f)); + return true; } - - if (blkid) { - *blkid = closest_id; - } - if (dist) { - *dist = closest_dist; - } - if (normal) { - *normal = closest_normal; - } - return true; } -void Chunk::Position(const Pos &pos) { - position = pos; -} -glm::mat4 Chunk::Transform(const Pos &offset) const { - return glm::translate((position - offset) * Extent()); -} +namespace { +BlockModel::Buffer buf; -void Chunk::CheckUpdate() { +} + +void Chunk::CheckUpdate() noexcept { if (dirty) { Update(); } - model.CheckUpdate(); } -void Chunk::Update() { +void Chunk::Update() noexcept { int vtx_count = 0, idx_count = 0; for (const auto &block : blocks) { const Shape *shape = Type(block).shape; vtx_count += shape->VertexCount(); idx_count += shape->VertexIndexCount(); } - model.Clear(); - model.Reserve(vtx_count, idx_count); - - Model::Index vtx_counter = 0; - for (size_t i = 0; i < Size(); ++i) { - const BlockType &type = Type(blocks[i]); - type.FillModel(model, ToCoords(i), vtx_counter); - vtx_counter += type.shape->VertexCount(); + buf.Clear(); + buf.Reserve(vtx_count, idx_count); + + int idx = 0; + BlockModel::Index vtx_counter = 0; + for (size_t z = 0; z < depth; ++z) { + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; ++x, ++idx) { + const BlockType &type = Type(BlockAt(idx)); + const Pos pos(x, y, z); + + if (!type.visible || Obstructed(pos).All()) continue; + + type.FillBlockModel(buf, ToTransform(pos, idx), vtx_counter); + size_t vtx_begin = vtx_counter; + vtx_counter += type.shape->VertexCount(); + + for (size_t vtx = vtx_begin; vtx < vtx_counter; ++vtx) { + buf.lights.emplace_back(GetVertexLight( + pos, + buf.vertices[vtx], + type.shape->VertexNormal(vtx - vtx_begin, BlockAt(idx).Transform()) + )); + } + } + } } - model.Invalidate(); + model.Update(buf); dirty = false; } +Block::FaceSet Chunk::Obstructed(const Pos &pos) const noexcept { + Block::FaceSet result; + + for (int f = 0; f < Block::FACE_COUNT; ++f) { + Block::Face face = Block::Face(f); + BlockLookup next(const_cast(this), pos, face); + if (next && next.GetType().FaceFilled(next.GetBlock(), Block::Opposite(face))) { + result.Set(face); + } + } + + return result; +} + +glm::mat4 Chunk::ToTransform(const Pos &pos, int idx) const noexcept { + return glm::translate(ToCoords(pos)) * BlockAt(idx).Transform(); +} + + +BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p) noexcept +: 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; + } + } + while (pos.x < 0) { + if (chunk->HasNeighbor(Block::FACE_LEFT)) { + chunk = &chunk->GetNeighbor(Block::FACE_LEFT); + pos.x += Chunk::width; + } else { + chunk = nullptr; + return; + } + } + while (pos.y >= Chunk::height) { + if (chunk->HasNeighbor(Block::FACE_UP)) { + chunk = &chunk->GetNeighbor(Block::FACE_UP); + pos.y -= Chunk::height; + } else { + chunk = nullptr; + return; + } + } + while (pos.y < 0) { + if (chunk->HasNeighbor(Block::FACE_DOWN)) { + chunk = &chunk->GetNeighbor(Block::FACE_DOWN); + pos.y += Chunk::height; + } else { + chunk = nullptr; + return; + } + } + while (pos.z >= Chunk::depth) { + if (chunk->HasNeighbor(Block::FACE_FRONT)) { + chunk = &chunk->GetNeighbor(Block::FACE_FRONT); + pos.z -= Chunk::depth; + } else { + chunk = nullptr; + return; + } + } + while (pos.z < 0) { + if (chunk->HasNeighbor(Block::FACE_BACK)) { + chunk = &chunk->GetNeighbor(Block::FACE_BACK); + pos.z += Chunk::depth; + } else { + chunk = nullptr; + return; + } + } +} + +BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p, Block::Face face) noexcept +: chunk(c), pos(p) { + pos += Block::FaceNormal(face); + if (!Chunk::InBounds(pos)) { + pos -= Block::FaceNormal(face) * Chunk::Extent(); + chunk = &chunk->GetNeighbor(face); + } +} + -ChunkLoader::ChunkLoader(const BlockTypeRegistry ®, const Generator &gen) +ChunkLoader::ChunkLoader(const Config &config, const BlockTypeRegistry ®, const Generator &gen) noexcept : base(0, 0, 0) , reg(reg) , gen(gen) , loaded() , to_generate() , to_free() -, load_dist(4) -, unload_dist(5) { +, load_dist(config.load_dist) +, unload_dist(config.unload_dist) { } @@ -158,12 +634,12 @@ namespace { struct ChunkLess { - explicit ChunkLess(const Chunk::Pos &base) + explicit ChunkLess(const Chunk::Pos &base) noexcept : base(base) { } - bool operator ()(const Chunk &a, const Chunk &b) const { - Chunk::Pos da(base - a.Position()); - Chunk::Pos db(base - b.Position()); + bool operator ()(const Chunk::Pos &a, const Chunk::Pos &b) const noexcept { + Chunk::Pos da(base - a); + Chunk::Pos db(base - b); return da.x * da.x + da.y * da.y + da.z * da.z < db.x * db.x + db.y * db.y + db.z * db.z; @@ -182,13 +658,55 @@ 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); + + // light testing + // for (int i = 0; i < 16; ++i) { + // for (int j = 0; j < 16; ++j) { + // loaded.back().SetBlock(Chunk::Pos{ i, j, 0 }, Block(1)); + // loaded.back().SetBlock(Chunk::Pos{ i, j, 15 }, Block(1)); + // loaded.back().SetBlock(Chunk::Pos{ 0, j, i }, Block(1)); + // loaded.back().SetBlock(Chunk::Pos{ 15, j, i }, Block(1)); + // } + // } + // loaded.back().SetBlock(Chunk::Pos{ 1, 0, 1 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 14, 0, 1 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 1, 0, 14 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 14, 0, 14 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 1, 15, 1 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 14, 15, 1 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 1, 15, 14 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 14, 15, 14 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 7, 7, 0 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 8, 7, 0 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 7, 8, 0 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 8, 8, 0 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 7, 7, 15 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 8, 7, 15 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 7, 8, 15 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 8, 8, 15 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 0, 7, 7 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 0, 7, 8 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 0, 8, 7 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 0, 8, 8 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 15, 7, 7 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 15, 7, 8 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 15, 8, 7 }, Block(13)); + // loaded.back().SetBlock(Chunk::Pos{ 15, 8, 8 }, Block(13)); + // loaded.back().Invalidate(); + // loaded.back().CheckUpdate(); + + // orientation testing + // for (int i = 0; i < Block::FACE_COUNT; ++i) { + // for (int j = 0; j < Block::TURN_COUNT; ++j) { + // loaded.back().BlockAt(512 * j + 2 * i) = Block(3 * (j + 1), Block::Face(i), Block::Turn(j)); + // } + // } + // loaded.back().Invalidate(); + // loaded.back().CheckUpdate(); } else { - to_generate.emplace_back(reg); - to_generate.back().Position(pos); + to_generate.emplace_back(pos); } } } @@ -196,7 +714,26 @@ void ChunkLoader::Generate(const Chunk::Pos &from, const Chunk::Pos &to) { to_generate.sort(ChunkLess(base)); } -Chunk *ChunkLoader::Loaded(const Chunk::Pos &pos) { +Chunk &ChunkLoader::Generate(const Chunk::Pos &pos) { + loaded.emplace_back(reg); + Chunk &chunk = loaded.back(); + chunk.Position(pos); + gen(chunk); + Insert(chunk); + return chunk; +} + +void ChunkLoader::Insert(Chunk &chunk) noexcept { + for (Chunk &other : loaded) { + chunk.SetNeighbor(other); + } +} + +void ChunkLoader::Remove(Chunk &chunk) noexcept { + chunk.Unlink(); +} + +Chunk *ChunkLoader::Loaded(const Chunk::Pos &pos) noexcept { for (Chunk &chunk : loaded) { if (chunk.Position() == pos) { return &chunk; @@ -205,19 +742,17 @@ Chunk *ChunkLoader::Loaded(const Chunk::Pos &pos) { return nullptr; } -Chunk *ChunkLoader::Queued(const Chunk::Pos &pos) { - for (Chunk &chunk : to_generate) { - if (chunk.Position() == pos) { - return &chunk; +bool ChunkLoader::Queued(const Chunk::Pos &pos) noexcept { + for (const Chunk::Pos &chunk : to_generate) { + if (chunk == pos) { + return true; } } return nullptr; } -Chunk *ChunkLoader::Known(const Chunk::Pos &pos) { - Chunk *chunk = Loaded(pos); - if (chunk) return chunk; - +bool ChunkLoader::Known(const Chunk::Pos &pos) noexcept { + if (Loaded(pos)) return true; return Queued(pos); } @@ -227,16 +762,14 @@ Chunk &ChunkLoader::ForceLoad(const Chunk::Pos &pos) { return *chunk; } - chunk = Queued(pos); - if (chunk) { - gen(*chunk); - return *chunk; + for (auto iter(to_generate.begin()), end(to_generate.end()); iter != end; ++iter) { + if (*iter == pos) { + to_generate.erase(iter); + break; + } } - 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) { @@ -251,6 +784,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 { @@ -259,28 +793,49 @@ void ChunkLoader::Rebase(const Chunk::Pos &new_base) { } // abort far away queued chunks for (auto iter(to_generate.begin()), end(to_generate.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) { + 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) { iter = to_generate.erase(iter); } else { ++iter; } } // add missing new chunks + GenerateSurrounding(base); +} + +void ChunkLoader::GenerateSurrounding(const Chunk::Pos &pos) { const Chunk::Pos offset(load_dist, load_dist, load_dist); - Generate(base - offset, base + offset); + Generate(pos - offset, pos + offset); } void ChunkLoader::Update() { - if (!to_generate.empty()) { - gen(to_generate.front()); - loaded.splice(loaded.end(), to_generate, to_generate.begin()); + if (to_generate.empty()) { + return; + } + + 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; + } } - if (!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); } }