diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index fa827724..c16dd5dd 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -84,7 +84,7 @@ std::shared_ptr create_debug_panel( return L"frustum-culling: "+std::wstring(culling ? L"on" : L"off"); })); panel->add(create_label([=]() { - return L"chunks: "+std::to_wstring(level->chunks->chunksCount)+ + return L"chunks: "+std::to_wstring(level->chunks->getChunksCount())+ L" visible: "+std::to_wstring(level->chunks->visible); })); panel->add(create_label([=]() { diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index a293ae2f..21cb56a5 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -79,7 +79,7 @@ WorldRenderer::~WorldRenderer() = default; bool WorldRenderer::drawChunk( size_t index, Camera* camera, Shader* shader, bool culling ) { - auto chunk = level->chunks->chunks[index]; + auto chunk = level->chunks->getChunks()[index]; if (!chunk->flags.lighted) { return false; } @@ -122,15 +122,16 @@ void WorldRenderer::drawChunks(Chunks* chunks, Camera* camera, Shader* shader) { // [warning] this whole method is not thread-safe for chunks std::vector indices; - for (size_t i = 0; i < chunks->volume; i++) { - if (chunks->chunks[i] == nullptr) continue; + for (size_t i = 0; i < chunks->getVolume(); i++) { + if (chunks->getChunks()[i] == nullptr) continue; indices.emplace_back(i); } float px = camera->position.x / static_cast(CHUNK_W) - 0.5f; float pz = camera->position.z / static_cast(CHUNK_D) - 0.5f; std::sort(indices.begin(), indices.end(), [chunks, px, pz](auto i, auto j) { - const auto a = chunks->chunks[i].get(); - const auto b = chunks->chunks[j].get(); + const auto& chunksBuffer = chunks->getChunks(); + const auto a = chunksBuffer[i].get(); + const auto b = chunksBuffer[j].get(); auto adx = (a->x - px); auto adz = (a->z - pz); auto bdx = (b->x - px); diff --git a/src/lighting/Lighting.cpp b/src/lighting/Lighting.cpp index df1e358d..6aad3049 100644 --- a/src/lighting/Lighting.cpp +++ b/src/lighting/Lighting.cpp @@ -23,8 +23,9 @@ Lighting::Lighting(const Content* content, Chunks* chunks) Lighting::~Lighting() = default; void Lighting::clear(){ - for (size_t index = 0; index < chunks->volume; index++){ - auto chunk = chunks->chunks[index]; + const auto& chunks = this->chunks->getChunks(); + for (size_t index = 0; index < chunks.size(); index++){ + auto chunk = chunks[index]; if (chunk == nullptr) continue; Lightmap& lightmap = chunk->lightmap; diff --git a/src/logic/BlocksController.cpp b/src/logic/BlocksController.cpp index 570c1f46..81cb592a 100644 --- a/src/logic/BlocksController.cpp +++ b/src/logic/BlocksController.cpp @@ -124,17 +124,16 @@ void BlocksController::randomTick( void BlocksController::randomTick(int tickid, int parts) { auto indices = level->content->getIndices(); - const int w = chunks->w; - const int d = chunks->d; + const auto& size = chunks->getSize(); int segments = 4; - for (uint z = padding; z < d - padding; z++) { - for (uint x = padding; x < w - padding; x++) { - int index = z * w + x; + for (uint z = padding; z < size.y - padding; z++) { + for (uint x = padding; x < size.x - padding; x++) { + int index = z * size.x + x; if ((index + tickid) % parts != 0) { continue; } - auto& chunk = chunks->chunks[index]; + auto& chunk = chunks->getChunks()[index]; if (chunk == nullptr || !chunk->flags.lighted) { continue; } diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index a1382df5..bd9e36c4 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -52,16 +52,15 @@ void ChunksController::update(int64_t maxDuration) { } bool ChunksController::loadVisible() { - const int w = chunks->w; - const int d = chunks->d; + const auto& size = chunks->getSize(); int nearX = 0; int nearZ = 0; - int minDistance = ((w - padding * 2) / 2) * ((w - padding * 2) / 2); - for (uint z = padding; z < d - padding; z++) { - for (uint x = padding; x < w - padding; x++) { - int index = z * w + x; - auto& chunk = chunks->chunks[index]; + int minDistance = ((size.x - padding * 2) / 2) * ((size.y - padding * 2) / 2); + for (uint z = padding; z < size.y - padding; z++) { + for (uint x = padding; x < size.x - padding; x++) { + int index = z * size.x + x; + auto& chunk = chunks->getChunks()[index]; if (chunk != nullptr) { if (chunk->flags.loaded && !chunk->flags.lighted) { if (buildLights(chunk)) { @@ -70,8 +69,8 @@ bool ChunksController::loadVisible() { } continue; } - int lx = x - w / 2; - int lz = z - d / 2; + int lx = x - size.x / 2; + int lz = z - size.y / 2; int distance = (lx * lx + lz * lz); if (distance < minDistance) { minDistance = distance; @@ -81,14 +80,12 @@ bool ChunksController::loadVisible() { } } - const auto& chunk = chunks->chunks[nearZ * w + nearX]; + const auto& chunk = chunks->getChunks()[nearZ * size.x + nearX]; if (chunk != nullptr) { return false; } - - const int ox = chunks->ox; - const int oz = chunks->oz; - createChunk(nearX + ox, nearZ + oz); + const auto& offset = chunks->getOffset(); + createChunk(nearX + offset.x, nearZ + offset.y); return true; } diff --git a/src/util/AreaMap2D.hpp b/src/util/AreaMap2D.hpp new file mode 100644 index 00000000..cbf90f5b --- /dev/null +++ b/src/util/AreaMap2D.hpp @@ -0,0 +1,163 @@ +#pragma once + +#include +#include +#include +#include + +namespace util { + template + using OutCallback = std::function; + + template + class AreaMap2D { + glm::vec<2, TCoord> offset; + glm::vec<2, TCoord> size; + std::vector firstBuffer; + std::vector secondBuffer; + OutCallback outCallback; + + size_t valuesCount = 0; + + void translate(const glm::vec<2, TCoord>& delta) { + if (delta.x == 0 && delta.y == 0) { + return; + } + std::fill(secondBuffer.begin(), secondBuffer.end(), T{}); + for (TCoord y = 0; y < size.y; y++) { + for (TCoord x = 0; x < size.x; x++) { + auto& value = firstBuffer[y * size.x + x]; + auto nx = x - delta.x; + auto ny = y - delta.y; + if (value == T{}) { + continue; + } + if (nx < 0 || ny < 0 || nx >= size.x || ny >= size.y) { + if (outCallback) { + outCallback(value); + } + valuesCount--; + continue; + } + secondBuffer[ny * size.x + nx] = value; + } + } + std::swap(firstBuffer, secondBuffer); + offset += delta; + } + public: + AreaMap2D(glm::vec<2, TCoord> size) + : size(size), + firstBuffer(size.x * size.y), secondBuffer(size.x * size.y) { + } + + const T* getIf(const glm::vec<2, TCoord>& pos) const { + auto localPos = pos - offset; + if (localPos.x < 0 || localPos.y < 0 || localPos.x >= size.x || + localPos.y >= size.y) { + return nullptr; + } + return &firstBuffer[localPos.y * size.x + localPos.x]; + } + + T get(const glm::vec<2, TCoord>& pos) { + auto localPos = pos - offset; + if (localPos.x < 0 || localPos.y < 0 || localPos.x >= size.x || + localPos.y >= size.y) { + return T{}; + } + return firstBuffer[localPos.y * size.x + localPos.x]; + } + + const T& require(const glm::vec<2, TCoord>& pos) const { + auto localPos = pos - offset; + if (localPos.x < 0 || localPos.y < 0 || localPos.x >= size.x || + localPos.y >= size.y) { + throw std::invalid_argument("position is out of window"); + } + return firstBuffer[localPos.y * size.x + localPos.x]; + } + + bool set(const glm::vec<2, TCoord>& pos, T value) { + auto localPos = pos - offset; + if (localPos.x < 0 || localPos.y < 0 || localPos.x >= size.x || + localPos.y >= size.y) { + return false; + } + auto& element = firstBuffer[localPos.y * size.x + localPos.x]; + if (!element) { + valuesCount++; + } + element = std::move(value); + return true; + } + + void setOutCallback(const OutCallback& callback) { + outCallback = callback; + } + + void resize(const glm::vec<2, TCoord>& newSize) { + if (newSize.x < size.x) { + TCoord delta = size.x - newSize.x; + translate({delta / 2, 0}); + translate({-delta, 0}); + translate({delta, 0}); + } + if (newSize.y < size.y) { + TCoord delta = size.y - newSize.y; + translate({0, delta / 2}); + translate({0, -delta}); + translate({0, delta}); + } + const TCoord newVolume = newSize.x * newSize.y; + std::vector newFirstBuffer(newVolume); + std::vector newSecondBuffer(newVolume); + for (TCoord y = 0; y < size.y && y < newSize.y; y++) { + for (TCoord x = 0; x < size.x && x < newSize.x; x++) { + newFirstBuffer[y * newSize.x + x] = firstBuffer[y * size.x + x]; + } + } + size = newSize; + firstBuffer = std::move(newFirstBuffer); + secondBuffer = std::move(newSecondBuffer); + } + + void setCenter(const glm::vec<2, TCoord>& center) { + auto delta = center - (offset + size / 2); + if (delta.x | delta.y) { + translate({delta.x, delta.y}); + } + } + + void clear() { + for (TCoord i = 0; i < size.x * size.y; i++) { + auto value = firstBuffer[i]; + firstBuffer[i] = {}; + if (outCallback) { + outCallback(value); + } + } + valuesCount = 0; + } + + const glm::vec<2, TCoord>& getOffset() const { + return offset; + } + + const glm::vec<2, TCoord>& getSize() const { + return size; + } + + const std::vector& getBuffer() const { + return firstBuffer; + } + + size_t count() const { + return valuesCount; + } + + TCoord area() const { + return size.x * size.y; + } + }; +} diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index f8b1b19d..be5c3d25 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -32,34 +32,31 @@ Chunks::Chunks( ) : level(level), indices(level->content->getIndices()), - chunks(w * d), - chunksSecond(w * d), - w(w), - d(d), - ox(ox), - oz(oz), + areaMap({w, d}), worldFiles(wfile) { - volume = static_cast(w) * static_cast(d); - chunksCount = 0; + areaMap.setCenter({ox-w/2, oz-d/2}); + areaMap.setOutCallback([this](const auto& chunk) { + save(chunk.get()); + }); } voxel* Chunks::get(int32_t x, int32_t y, int32_t z) const { - x -= ox * CHUNK_W; - z -= oz * CHUNK_D; - int cx = floordiv(x, CHUNK_W); - int cy = floordiv(y, CHUNK_H); - int cz = floordiv(z, CHUNK_D); - if (cx < 0 || cy < 0 || cz < 0 || cx >= int(w) || cy >= 1 || cz >= int(d)) { + if (y < 0 || y >= CHUNK_H) { return nullptr; } - auto& chunk = chunks[cz * w + cx]; // not thread safe + int cx = floordiv(x, CHUNK_W); + int cz = floordiv(z, CHUNK_D); + auto ptr = areaMap.getIf({cx, cz}); + if (ptr == nullptr) { + return nullptr; + } + Chunk* chunk = ptr->get(); // not thread safe if (chunk == nullptr) { return nullptr; } int lx = x - cx * CHUNK_W; - int ly = y - cy * CHUNK_H; int lz = z - cz * CHUNK_D; - return &chunk->voxels[(ly * CHUNK_D + lz) * CHUNK_W + lx]; + return &chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; } const AABB* Chunks::isObstacleAt(float x, float y, float z) { @@ -114,61 +111,62 @@ bool Chunks::isObstacleBlock(int32_t x, int32_t y, int32_t z) { } ubyte Chunks::getLight(int32_t x, int32_t y, int32_t z, int channel) { - x -= ox * CHUNK_W; - z -= oz * CHUNK_D; - int cx = floordiv(x, CHUNK_W); - int cy = floordiv(y, CHUNK_H); - int cz = floordiv(z, CHUNK_D); - if (cx < 0 || cy < 0 || cz < 0 || cx >= int(w) || cy >= 1 || cz >= int(d)) { + if (y < 0 || y >= CHUNK_H) { return 0; } - const auto& chunk = chunks[(cy * d + cz) * w + cx]; + int cx = floordiv(x, CHUNK_W); + int cz = floordiv(z, CHUNK_D); + + auto ptr = areaMap.getIf({cx, cz}); + if (ptr == nullptr) { + return 0; + } + Chunk* chunk = ptr->get(); if (chunk == nullptr) { return 0; } int lx = x - cx * CHUNK_W; - int ly = y - cy * CHUNK_H; int lz = z - cz * CHUNK_D; - return chunk->lightmap.get(lx, ly, lz, channel); + return chunk->lightmap.get(lx, y, lz, channel); } light_t Chunks::getLight(int32_t x, int32_t y, int32_t z) { - x -= ox * CHUNK_W; - z -= oz * CHUNK_D; - int cx = floordiv(x, CHUNK_W); - int cy = floordiv(y, CHUNK_H); - int cz = floordiv(z, CHUNK_D); - if (cx < 0 || cy < 0 || cz < 0 || cx >= int(w) || cy >= 1 || cz >= int(d)) { + if (y < 0 || y >= CHUNK_H) { return 0; } - const auto& chunk = chunks[(cy * d + cz) * w + cx]; + int cx = floordiv(x, CHUNK_W); + int cz = floordiv(z, CHUNK_D); + + auto ptr = areaMap.getIf({cx, cz}); + if (ptr == nullptr) { + return 0; + } + Chunk* chunk = ptr->get(); if (chunk == nullptr) { return 0; } int lx = x - cx * CHUNK_W; - int ly = y - cy * CHUNK_H; int lz = z - cz * CHUNK_D; - return chunk->lightmap.get(lx, ly, lz); + return chunk->lightmap.get(lx, y, lz); } Chunk* Chunks::getChunkByVoxel(int32_t x, int32_t y, int32_t z) { - if (y < 0 || y >= CHUNK_H) return nullptr; - x -= ox * CHUNK_W; - z -= oz * CHUNK_D; + if (y < 0 || y >= CHUNK_H) { + return nullptr; + } int cx = floordiv(x, CHUNK_W); int cz = floordiv(z, CHUNK_D); - if (cx < 0 || cz < 0 || cx >= int(w) || cz >= int(d)) return nullptr; - return chunks[cz * w + cx].get(); + if (auto ptr = areaMap.getIf({cx, cz})) { + return ptr->get(); + } + return nullptr; } Chunk* Chunks::getChunk(int x, int z) { - x -= ox; - z -= oz; - if (x < 0 || z < 0 || x >= static_cast(w) || - z >= static_cast(d)) { - return nullptr; + if (auto ptr = areaMap.getIf({x, z})) { + return ptr->get(); } - return chunks[z * w + x].get(); + return nullptr; } glm::ivec3 Chunks::seekOrigin( @@ -355,15 +353,13 @@ void Chunks::set( } int32_t gx = x; int32_t gz = z; - x -= ox * CHUNK_W; - z -= oz * CHUNK_D; int cx = floordiv(x, CHUNK_W); int cz = floordiv(z, CHUNK_D); - if (cx < 0 || cz < 0 || cx >= static_cast(w) || - cz >= static_cast(d)) { + auto ptr = areaMap.getIf({cx, cz}); + if (ptr == nullptr) { return; } - Chunk* chunk = chunks[cz * w + cx].get(); + Chunk* chunk = ptr->get(); if (chunk == nullptr) { return; } @@ -396,15 +392,18 @@ void Chunks::set( else if (id == 0) chunk->updateHeights(); - if (lx == 0 && (chunk = getChunk(cx + ox - 1, cz + oz))) + if (lx == 0 && (chunk = getChunk(cx - 1, cz))) { chunk->flags.modified = true; - if (lz == 0 && (chunk = getChunk(cx + ox, cz + oz - 1))) + } + if (lz == 0 && (chunk = getChunk(cx, cz - 1))) { chunk->flags.modified = true; - - if (lx == CHUNK_W - 1 && (chunk = getChunk(cx + ox + 1, cz + oz))) + } + if (lx == CHUNK_W - 1 && (chunk = getChunk(cx, cz))) { chunk->flags.modified = true; - if (lz == CHUNK_D - 1 && (chunk = getChunk(cx + ox, cz + oz + 1))) + } + if (lz == CHUNK_D - 1 && (chunk = getChunk(cx, cz + 1))) { chunk->flags.modified = true; + } } voxel* Chunks::rayCast( @@ -643,97 +642,19 @@ glm::vec3 Chunks::rayCastToObstacle( } void Chunks::setCenter(int32_t x, int32_t z) { - int cx = floordiv(x, CHUNK_W); - int cz = floordiv(z, CHUNK_D); - cx -= ox + w / 2; - cz -= oz + d / 2; - if (cx | cz) { - translate(cx, cz); - } -} - -void Chunks::translate(int32_t dx, int32_t dz) { - for (uint i = 0; i < volume; i++) { - chunksSecond[i] = nullptr; - } - for (uint32_t z = 0; z < d; z++) { - for (uint32_t x = 0; x < w; x++) { - auto& chunk = chunks[z * w + x]; - int nx = x - dx; - int nz = z - dz; - if (chunk == nullptr) continue; - if (nx < 0 || nz < 0 || nx >= static_cast(w) || - nz >= static_cast(d)) { - level->events->trigger(EVT_CHUNK_HIDDEN, chunk.get()); - save(chunk.get()); - chunksCount--; - continue; - } - chunksSecond[nz * w + nx] = chunk; - } - } - std::swap(chunks, chunksSecond); - - ox += dx; - oz += dz; + areaMap.setCenter({floordiv(x, CHUNK_W), floordiv(z, CHUNK_D)}); } void Chunks::resize(uint32_t newW, uint32_t newD) { - if (newW < w) { - int delta = w - newW; - translate(delta / 2, 0); - translate(-delta, 0); - translate(delta, 0); - } - if (newD < d) { - int delta = d - newD; - translate(0, delta / 2); - translate(0, -delta); - translate(0, delta); - } - const int newVolume = newW * newD; - std::vector> newChunks(newVolume); - std::vector> newChunksSecond(newVolume); - for (int z = 0; z < static_cast(d) && z < static_cast(newD); - z++) { - for (int x = 0; x < static_cast(w) && x < static_cast(newW); - x++) { - newChunks[z * newW + x] = chunks[z * w + x]; - } - } - w = newW; - d = newD; - volume = newVolume; - chunks = std::move(newChunks); - chunksSecond = std::move(newChunksSecond); -} - -void Chunks::_setOffset(int32_t x, int32_t z) { - ox = x; - oz = z; + areaMap.resize({newW, newD}); } bool Chunks::putChunk(const std::shared_ptr& chunk) { - int x = chunk->x; - int z = chunk->z; - x -= ox; - z -= oz; - if (x < 0 || z < 0 || x >= static_cast(w) || - z >= static_cast(d)) { - return false; - } - chunks[z * w + x] = chunk; - chunksCount++; - return true; + return areaMap.set({chunk->x, chunk->z}, chunk); } void Chunks::saveAndClear() { - for (size_t i = 0; i < volume; i++) { - auto chunk = chunks[i].get(); - chunks[i] = nullptr; - save(chunk); - } - chunksCount = 0; + areaMap.clear(); } void Chunks::save(Chunk* chunk) { @@ -760,7 +681,8 @@ void Chunks::save(Chunk* chunk) { } void Chunks::saveAll() { - for (size_t i = 0; i < volume; i++) { + const auto& chunks = areaMap.getBuffer(); + for (size_t i = 0; i < areaMap.area(); i++) { if (auto& chunk = chunks[i]) { save(chunk.get()); } diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index 0d3e07e4..5214f81e 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -9,6 +9,7 @@ #include "typedefs.hpp" #include "voxel.hpp" +#include "util/AreaMap2D.hpp" class VoxelRenderer; @@ -33,14 +34,10 @@ class Chunks { void setRotationExtended( const Block& def, blockstate state, glm::ivec3 origin, uint8_t rotation ); + + util::AreaMap2D> areaMap; public: - std::vector> chunks; - std::vector> chunksSecond; - size_t volume; - size_t chunksCount; size_t visible = 0; - uint32_t w, d; - int32_t ox, oz; WorldFiles* worldFiles; Chunks( @@ -105,14 +102,30 @@ public: bool isReplaceableBlock(int32_t x, int32_t y, int32_t z); bool isObstacleBlock(int32_t x, int32_t y, int32_t z); - // does not move chunks inside - void _setOffset(int32_t x, int32_t z); - void setCenter(int32_t x, int32_t z); - void translate(int32_t x, int32_t z); void resize(uint32_t newW, uint32_t newD); void saveAndClear(); void save(Chunk* chunk); void saveAll(); + + const std::vector>& getChunks() const { + return areaMap.getBuffer(); + } + + const glm::ivec2& getSize() const { + return areaMap.getSize(); + } + + const glm::ivec2& getOffset() const { + return areaMap.getOffset(); + } + + size_t getChunksCount() const { + return areaMap.count(); + } + + size_t getVolume() const { + return areaMap.area(); + } }; diff --git a/src/world/Level.cpp b/src/world/Level.cpp index 6d9b1f03..190559d7 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -86,7 +86,7 @@ void Level::loadMatrix(int32_t x, int32_t z, uint32_t radius) { (settings.chunks.loadDistance.get() + settings.chunks.padding.get()) * 2LL ); - if (chunks->w != diameter) { + if (chunks->getSize().x != diameter) { chunks->resize(diameter, diameter); } } diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 5fef618b..f699957d 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -104,7 +104,7 @@ void WorldGenerator::generateHeightmap( } void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { - timeutil::ScopeLogTimer log(555); + //timeutil::ScopeLogTimer log(555); auto prototype = generatePrototype(chunkX, chunkZ); generateHeightmap(prototype.get(), chunkX, chunkZ); diff --git a/test/util/AreaMap2D.cpp b/test/util/AreaMap2D.cpp new file mode 100644 index 00000000..4fdd78fe --- /dev/null +++ b/test/util/AreaMap2D.cpp @@ -0,0 +1,7 @@ +#include + +#include "util/AreaMap2D.hpp" + +TEST(AreaMap2D, BaseTest) { + util::AreaMap2D window({6, 6}); +}