add AreaMap2D

This commit is contained in:
MihailRis 2024-09-09 18:02:08 +03:00
parent f70ac5c190
commit c3569b5dd4
11 changed files with 282 additions and 179 deletions

View File

@ -84,7 +84,7 @@ std::shared_ptr<UINode> 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([=]() {

View File

@ -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<size_t> 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<float>(CHUNK_W) - 0.5f;
float pz = camera->position.z / static_cast<float>(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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

163
src/util/AreaMap2D.hpp Normal file
View File

@ -0,0 +1,163 @@
#pragma once
#include <vector>
#include <stdexcept>
#include <functional>
#include <glm/glm.hpp>
namespace util {
template<class T>
using OutCallback = std::function<void(const T&)>;
template<class T, typename TCoord=int>
class AreaMap2D {
glm::vec<2, TCoord> offset;
glm::vec<2, TCoord> size;
std::vector<T> firstBuffer;
std::vector<T> secondBuffer;
OutCallback<T> 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<T>& 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<T> newFirstBuffer(newVolume);
std::vector<T> 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<T>& getBuffer() const {
return firstBuffer;
}
size_t count() const {
return valuesCount;
}
TCoord area() const {
return size.x * size.y;
}
};
}

View File

@ -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<size_t>(w) * static_cast<size_t>(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<int>(w) ||
z >= static_cast<int>(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<int>(w) ||
cz >= static_cast<int>(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<int>(w) ||
nz >= static_cast<int>(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<std::shared_ptr<Chunk>> newChunks(newVolume);
std::vector<std::shared_ptr<Chunk>> newChunksSecond(newVolume);
for (int z = 0; z < static_cast<int>(d) && z < static_cast<int>(newD);
z++) {
for (int x = 0; x < static_cast<int>(w) && x < static_cast<int>(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>& chunk) {
int x = chunk->x;
int z = chunk->z;
x -= ox;
z -= oz;
if (x < 0 || z < 0 || x >= static_cast<int>(w) ||
z >= static_cast<int>(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());
}

View File

@ -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<std::shared_ptr<Chunk>> areaMap;
public:
std::vector<std::shared_ptr<Chunk>> chunks;
std::vector<std::shared_ptr<Chunk>> 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<std::shared_ptr<Chunk>>& 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();
}
};

View File

@ -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);
}
}

View File

@ -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);

7
test/util/AreaMap2D.cpp Normal file
View File

@ -0,0 +1,7 @@
#include <gtest/gtest.h>
#include "util/AreaMap2D.hpp"
TEST(AreaMap2D, BaseTest) {
util::AreaMap2D<int> window({6, 6});
}