stabilize delays of chunks meshes building

This commit is contained in:
MihailRis 2025-12-29 20:41:47 +03:00
parent f47ccd5f30
commit 88544183d0
13 changed files with 335 additions and 191 deletions

View file

@ -353,7 +353,7 @@ std::shared_ptr<Task> AssetsLoader::startTask(runnable onDone) {
auto pool = auto pool =
std::make_shared<util::ThreadPool<aloader_entry, assetload::postfunc>>( std::make_shared<util::ThreadPool<aloader_entry, assetload::postfunc>>(
"assets-loader-pool", "assets-loader-pool",
[=]() { return std::make_shared<LoaderWorker>(this); }, [=]() { return std::make_unique<LoaderWorker>(this); },
[this](assetload::postfunc&& func) { func(&assets); } [this](assetload::postfunc&& func) { func(&assets); }
); );
pool->setOnComplete(std::move(onDone)); pool->setOnComplete(std::move(onDone));

View file

@ -42,21 +42,22 @@ void BlocksRenderer::vertex(
const glm::vec3& normal, const glm::vec3& normal,
float emission float emission
) { ) {
vertexBuffer[vertexCount].position = coord;
vertexBuffer[vertexCount].uv = {u,v}; vertexBuffer[vertexCount++] = {
coord,
vertexBuffer[vertexCount].normal[0] = static_cast<uint8_t>(normal.r * 127 + 128); {u, v},
vertexBuffer[vertexCount].normal[1] = static_cast<uint8_t>(normal.g * 127 + 128); {
vertexBuffer[vertexCount].normal[2] = static_cast<uint8_t>(normal.b * 127 + 128); static_cast<uint8_t>(light.r * 255),
vertexBuffer[vertexCount].normal[3] = static_cast<uint8_t>(emission * 255); static_cast<uint8_t>(light.g * 255),
static_cast<uint8_t>(light.b * 255),
vertexBuffer[vertexCount].color[0] = static_cast<uint8_t>(light.r * 255); static_cast<uint8_t>(light.a * 255),
vertexBuffer[vertexCount].color[1] = static_cast<uint8_t>(light.g * 255); }, {
vertexBuffer[vertexCount].color[2] = static_cast<uint8_t>(light.b * 255); static_cast<uint8_t>(normal.x * 127 + 128),
vertexBuffer[vertexCount].color[3] = static_cast<uint8_t>(light.a * 255); static_cast<uint8_t>(normal.y * 127 + 128),
static_cast<uint8_t>(normal.z * 127 + 128),
vertexCount++; static_cast<uint8_t>(emission * 255)
}
};
} }
void BlocksRenderer::index(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e, uint32_t f) { void BlocksRenderer::index(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e, uint32_t f) {
@ -434,28 +435,11 @@ void BlocksRenderer::blockCube(
} }
} }
bool BlocksRenderer::isOpenForLight(int x, int y, int z) const {
blockid_t id = voxelsBuffer->pickBlockId(chunk->x * CHUNK_W + x,
y,
chunk->z * CHUNK_D + z);
if (id == BLOCK_VOID) {
return false;
}
const Block& block = *blockDefsCache[id];
if (block.lightPassing) {
return true;
}
return !id;
}
glm::vec4 BlocksRenderer::pickLight(int x, int y, int z) const { glm::vec4 BlocksRenderer::pickLight(int x, int y, int z) const {
if (isOpenForLight(x, y, z)) { light_t light = voxelsBuffer->pickLight(
light_t light = voxelsBuffer->pickLight(chunk->x * CHUNK_W + x, y, chunk->x * CHUNK_W + x, y, chunk->z * CHUNK_D + z
chunk->z * CHUNK_D + z); );
return Lightmap::extractNormalized(light); return light ? Lightmap::extractNormalized(light) : glm::vec4(0.0f);
} else {
return glm::vec4(0.0f);
}
} }
glm::vec4 BlocksRenderer::pickLight(const glm::ivec3& coord) const { glm::vec4 BlocksRenderer::pickLight(const glm::ivec3& coord) const {
@ -694,7 +678,9 @@ SortingMeshData BlocksRenderer::renderTranslucent(
return sortingMesh; return sortingMesh;
} }
void BlocksRenderer::build(const Chunk* chunk, const VoxelsVolume& volume) { void BlocksRenderer::build(
const Chunk* chunk, const VoxelsRenderVolume& volume
) {
this->chunk = chunk; this->chunk = chunk;
this->voxelsBuffer = &volume; this->voxelsBuffer = &volume;
if (voxelsBuffer->pickBlockId( if (voxelsBuffer->pickBlockId(
@ -707,16 +693,17 @@ void BlocksRenderer::build(const Chunk* chunk, const VoxelsVolume& volume) {
int totalBegin = chunk->bottom * (CHUNK_W * CHUNK_D); int totalBegin = chunk->bottom * (CHUNK_W * CHUNK_D);
int totalEnd = chunk->top * (CHUNK_W * CHUNK_D); int totalEnd = chunk->top * (CHUNK_W * CHUNK_D);
bool hasTranslucent = false;
int beginEnds[256][2] {}; int beginEnds[256][2] {};
for (int i = totalBegin; i < totalEnd; i++) { for (int i = totalBegin; i < totalEnd; i++) {
const voxel& vox = voxels[i]; const voxel& vox = voxels[i];
blockid_t id = vox.id; blockid_t id = vox.id;
const auto& def = *blockDefsCache[id]; const auto& def = *blockDefsCache[id];
const auto& variant = def.getVariantByBits(vox.state.userbits); const auto& variant = def.getVariantByBits(vox.state.userbits);
hasTranslucent = def.translucent || hasTranslucent;
if (beginEnds[variant.drawGroup][0] == 0) { if (beginEnds[variant.drawGroup][0] == 0) {
beginEnds[variant.drawGroup][0] = i+1; beginEnds[variant.drawGroup][0] = i + 1;
} }
beginEnds[variant.drawGroup][1] = i; beginEnds[variant.drawGroup][1] = i;
} }
@ -729,7 +716,11 @@ void BlocksRenderer::build(const Chunk* chunk, const VoxelsVolume& volume) {
denseRender = false; denseRender = false;
densePass = false; densePass = false;
sortingMesh = renderTranslucent(voxels, beginEnds); if (hasTranslucent) {
sortingMesh = renderTranslucent(voxels, beginEnds);
} else {
sortingMesh = {};
}
overflow = false; overflow = false;
vertexCount = 0; vertexCount = 0;
@ -775,7 +766,7 @@ ChunkMeshData BlocksRenderer::createMesh() {
} }
ChunkMesh BlocksRenderer::render( ChunkMesh BlocksRenderer::render(
const Chunk* chunk, const VoxelsVolume& volume const Chunk* chunk, const VoxelsRenderVolume& volume
) { ) {
build(chunk, volume); build(chunk, volume);

View file

@ -17,7 +17,6 @@ class Content;
class Block; class Block;
class Chunk; class Chunk;
class Chunks; class Chunks;
class VoxelsVolume;
class ContentGfxCache; class ContentGfxCache;
struct UVRegion; struct UVRegion;
@ -31,9 +30,9 @@ public:
); );
~BlocksRenderer(); ~BlocksRenderer();
void build(const Chunk* chunk, const VoxelsVolume& volume); void build(const Chunk* chunk, const VoxelsRenderVolume& volume);
ChunkMesh render( ChunkMesh render(
const Chunk* chunk, const VoxelsVolume& volume const Chunk* chunk, const VoxelsRenderVolume& volume
); );
ChunkMeshData createMesh(); ChunkMeshData createMesh();
@ -58,7 +57,7 @@ private:
bool densePass = false; bool densePass = false;
bool denseRender = false; bool denseRender = false;
const Chunk* chunk = nullptr; const Chunk* chunk = nullptr;
const VoxelsVolume* voxelsBuffer = nullptr; const VoxelsRenderVolume* voxelsBuffer = nullptr;
const Block* const* blockDefsCache; const Block* const* blockDefsCache;
const ContentGfxCache& cache; const ContentGfxCache& cache;
@ -143,8 +142,6 @@ private:
bool ao bool ao
); );
bool isOpenForLight(int x, int y, int z) const;
// Does block allow to see other blocks sides (is it transparent) // Does block allow to see other blocks sides (is it transparent)
inline bool isOpen(const glm::ivec3& pos, const Block& def, const Variant& variant) const { inline bool isOpen(const glm::ivec3& pos, const Block& def, const Variant& variant) const {
auto vox = voxelsBuffer->pickBlock( auto vox = voxelsBuffer->pickBlock(

View file

@ -19,6 +19,8 @@ static debug::Logger logger("chunks-render");
size_t ChunksRenderer::visibleChunks = 0; size_t ChunksRenderer::visibleChunks = 0;
static constexpr inline size_t MAX_CHUNKS_ENQUEUED_IN_FRAME = 4;
class RendererWorker : public util::Worker<RendererJob, RendererResult> { class RendererWorker : public util::Worker<RendererJob, RendererResult> {
BlocksRenderer renderer; BlocksRenderer renderer;
public: public:
@ -51,8 +53,7 @@ public:
} }
}; };
static util::ObjectsPool<VoxelsVolume> voxelsVolumesPool {}; static util::ObjectsPool<VoxelsRenderVolume> voxelsVolumesPool {};
static inline const int VOXELS_BUFFER_PADDING = 2;
ChunksRenderer::ChunksRenderer( ChunksRenderer::ChunksRenderer(
const Level* level, const Level* level,
@ -69,7 +70,7 @@ ChunksRenderer::ChunksRenderer(
threadPool( threadPool(
"chunks-render-pool", "chunks-render-pool",
[&]() { [&]() {
return std::make_shared<RendererWorker>( return std::make_unique<RendererWorker>(
*level, cache, settings *level, cache, settings
); );
}, },
@ -105,14 +106,10 @@ ChunksRenderer::ChunksRenderer(
ChunksRenderer::~ChunksRenderer() = default; ChunksRenderer::~ChunksRenderer() = default;
std::shared_ptr<VoxelsVolume> ChunksRenderer::prepareVoxelsVolume( std::shared_ptr<VoxelsRenderVolume> ChunksRenderer::prepareVoxelsVolume(
const Chunk& chunk const Chunk& chunk
) { ) {
auto voxelsBuffer = voxelsVolumesPool.create( auto voxelsBuffer = voxelsVolumesPool.create();
CHUNK_W + VOXELS_BUFFER_PADDING * 2,
CHUNK_H,
CHUNK_D + VOXELS_BUFFER_PADDING * 2
);
voxelsBuffer->setPosition( voxelsBuffer->setPosition(
chunk.x * CHUNK_W - VOXELS_BUFFER_PADDING, 0, chunk.x * CHUNK_W - VOXELS_BUFFER_PADDING, 0,
chunk.z * CHUNK_D - VOXELS_BUFFER_PADDING chunk.z * CHUNK_D - VOXELS_BUFFER_PADDING
@ -123,8 +120,10 @@ std::shared_ptr<VoxelsVolume> ChunksRenderer::prepareVoxelsVolume(
return voxelsBuffer; return voxelsBuffer;
} }
#include "util/timeutil.hpp"
const Mesh<ChunkVertex>* ChunksRenderer::render( const Mesh<ChunkVertex>* ChunksRenderer::render(
const std::shared_ptr<Chunk>& chunk, bool important const std::shared_ptr<Chunk>& chunk, bool important, bool lowPriority
) { ) {
glm::ivec2 key(chunk->x, chunk->z); glm::ivec2 key(chunk->x, chunk->z);
chunk->flags.modified = false; chunk->flags.modified = false;
@ -136,16 +135,16 @@ const Mesh<ChunkVertex>* ChunksRenderer::render(
std::move(mesh.mesh), std::move(mesh.sortingMeshData), nullptr}; std::move(mesh.mesh), std::move(mesh.sortingMeshData), nullptr};
return meshes[key].mesh.get(); return meshes[key].mesh.get();
} }
if (inwork.find(key) != inwork.end()) { if (inwork.find(key) != inwork.end() ||
((inwork.size() >= threadPool.getWorkTotal() ||
enqueuedInFrame >= MAX_CHUNKS_ENQUEUED_IN_FRAME) &&
lowPriority)) {
return nullptr; return nullptr;
} }
enqueuedInFrame++;
auto voxelsBuffer = prepareVoxelsVolume(*chunk); auto voxelsBuffer = prepareVoxelsVolume(*chunk);
inwork[key] = true;
chunks.getVoxels(
*voxelsBuffer, settings.graphics.backlight.get(), chunk->top + 1
);
threadPool.enqueueJob({chunk, std::move(voxelsBuffer)}); threadPool.enqueueJob({chunk, std::move(voxelsBuffer)});
inwork[key] = true;
return nullptr; return nullptr;
} }
@ -163,20 +162,21 @@ void ChunksRenderer::clear() {
} }
const Mesh<ChunkVertex>* ChunksRenderer::getOrRender( const Mesh<ChunkVertex>* ChunksRenderer::getOrRender(
const std::shared_ptr<Chunk>& chunk, bool important const std::shared_ptr<Chunk>& chunk, bool important, bool lowPriority
) { ) {
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z)); auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
if (found == meshes.end()) { if (found == meshes.end()) {
return render(chunk, important); return render(chunk, important, lowPriority);
} }
if (chunk->flags.modified && chunk->flags.lighted) { if (chunk->flags.modified && chunk->flags.lighted) {
render(chunk, important); render(chunk, important, lowPriority);
} }
return found->second.mesh.get(); return found->second.mesh.get();
} }
void ChunksRenderer::update() { void ChunksRenderer::update() {
threadPool.pullResults(); threadPool.pullResults();
enqueuedInFrame = 0;
} }
const Mesh<ChunkVertex>* ChunksRenderer::retrieveChunk( const Mesh<ChunkVertex>* ChunksRenderer::retrieveChunk(
@ -202,7 +202,11 @@ const Mesh<ChunkVertex>* ChunksRenderer::retrieveChunk(
(chunk->z + 0.5f) * CHUNK_D (chunk->z + 0.5f) * CHUNK_D
) )
); );
auto mesh = getOrRender(chunk, distance < CHUNK_W * 1.5f); auto mesh = getOrRender(
chunk,
distance < CHUNK_W * 1.5f,
distance > CHUNK_W * settings.chunks.loadDistance.get() * 0.5
);
if (mesh == nullptr) { if (mesh == nullptr) {
return nullptr; return nullptr;
} }

View file

@ -21,7 +21,6 @@ class Chunks;
class Frustum; class Frustum;
class BlocksRenderer; class BlocksRenderer;
class ContentGfxCache; class ContentGfxCache;
class VoxelsVolume;
struct EngineSettings; struct EngineSettings;
struct ChunksSortEntry { struct ChunksSortEntry {
@ -41,7 +40,7 @@ struct RendererResult {
struct RendererJob { struct RendererJob {
std::shared_ptr<Chunk> chunk; std::shared_ptr<Chunk> chunk;
std::shared_ptr<VoxelsVolume> volume; std::shared_ptr<VoxelsRenderVolume> volume;
}; };
class ChunksRenderer { class ChunksRenderer {
@ -58,7 +57,8 @@ class ChunksRenderer {
const Mesh<ChunkVertex>* retrieveChunk( const Mesh<ChunkVertex>* retrieveChunk(
size_t index, const Camera& camera, bool culling size_t index, const Camera& camera, bool culling
); );
std::shared_ptr<VoxelsVolume> prepareVoxelsVolume(const Chunk& chunk); std::shared_ptr<VoxelsRenderVolume> prepareVoxelsVolume(const Chunk& chunk);
size_t enqueuedInFrame = 0;
public: public:
ChunksRenderer( ChunksRenderer(
const Level* level, const Level* level,
@ -71,13 +71,13 @@ public:
virtual ~ChunksRenderer(); virtual ~ChunksRenderer();
const Mesh<ChunkVertex>* render( const Mesh<ChunkVertex>* render(
const std::shared_ptr<Chunk>& chunk, bool important const std::shared_ptr<Chunk>& chunk, bool important, bool lowPriority
); );
void unload(const Chunk* chunk); void unload(const Chunk* chunk);
void clear(); void clear();
const Mesh<ChunkVertex>* getOrRender( const Mesh<ChunkVertex>* getOrRender(
const std::shared_ptr<Chunk>& chunk, bool important const std::shared_ptr<Chunk>& chunk, bool important, bool lowPriority
); );
void drawShadowsPass( void drawShadowsPass(

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "constants.hpp"
#include "graphics/core/MeshData.hpp" #include "graphics/core/MeshData.hpp"
#include "util/Buffer.hpp" #include "util/Buffer.hpp"
@ -51,3 +52,12 @@ struct ChunkMesh {
SortingMeshData sortingMeshData; SortingMeshData sortingMeshData;
std::unique_ptr<Mesh<ChunkVertex> > sortedMesh; std::unique_ptr<Mesh<ChunkVertex> > sortedMesh;
}; };
inline constexpr int VOXELS_BUFFER_PADDING = 2;
template<int, int, int> class StaticVoxelsVolume;
using VoxelsRenderVolume = StaticVoxelsVolume<
CHUNK_W + VOXELS_BUFFER_PADDING * 2,
CHUNK_H,
CHUNK_D + VOXELS_BUFFER_PADDING * 2>;

View file

@ -50,6 +50,10 @@ namespace util {
size_t countTotal() const { size_t countTotal() const {
return objects.size(); return objects.size();
} }
size_t countFree() const {
return freeObjects.size();
}
private: private:
std::vector<std::unique_ptr<void, AlignedDeleter>> objects; std::vector<std::unique_ptr<void, AlignedDeleter>> objects;
std::queue<void*> freeObjects; std::queue<void*> freeObjects;

View file

@ -52,7 +52,7 @@ namespace util {
bool standaloneResults = true; bool standaloneResults = true;
bool stopOnFail = true; bool stopOnFail = true;
void threadLoop(int index, std::shared_ptr<Worker<T, R>> worker) { void threadLoop(int index, std::unique_ptr<Worker<T, R>> worker) {
std::condition_variable variable; std::condition_variable variable;
std::mutex mutex; std::mutex mutex;
bool locked = false; bool locked = false;
@ -76,7 +76,7 @@ namespace util {
{ {
std::lock_guard<std::mutex> lock(resultsMutex); std::lock_guard<std::mutex> lock(resultsMutex);
results.push(ThreadPoolResult<T, R> { results.push(ThreadPoolResult<T, R> {
job, variable, index, locked, result}); job, variable, index, locked, std::move(result)});
if (!standaloneResults) { if (!standaloneResults) {
locked = true; locked = true;
} }
@ -115,7 +115,7 @@ namespace util {
/// unlimited, -2 is half of auto count, -4 is quarter. /// unlimited, -2 is half of auto count, -4 is quarter.
ThreadPool( ThreadPool(
std::string name, std::string name,
supplier<std::shared_ptr<Worker<T, R>>> workersSupplier, supplier<std::unique_ptr<Worker<T, R>>> workersSupplier,
consumer<R&&> resultConsumer, consumer<R&&> resultConsumer,
int maxWorkers=UNLIMITED int maxWorkers=UNLIMITED
) )
@ -236,7 +236,7 @@ namespace util {
return resultsProcessed; return resultsProcessed;
} }
void enqueueJob(T job) { void enqueueJob(T&& job) {
{ {
std::lock_guard<std::mutex> lock(jobsMutex); std::lock_guard<std::mutex> lock(jobsMutex);
jobs.push(std::move(job)); jobs.push(std::move(job));

View file

@ -335,26 +335,106 @@ bool Chunks::putChunk(const std::shared_ptr<Chunk>& chunk) {
return false; return false;
} }
// reduce nesting on next modification static void fill_with_void(
// 25.06.2024: not now voxel* voxels,
// 11.11.2024: not now light_t* lights,
// 12.12.2025: not now const glm::ivec3& pos,
void Chunks::getVoxels(VoxelsVolume& volume, bool backlight, int top) const { const glm::ivec3& size,
voxel* voxels = volume.getVoxels(); int cx,
light_t* lights = volume.getLights(); int cz
int x = volume.getX(); ) {
int y = volume.getY(); for (int ly = pos.y; ly < pos.y + size.y; ly++) {
int z = volume.getZ(); for (int lz = std::max(pos.z, cz * CHUNK_D);
lz < std::min(pos.z + size.z, (cz + 1) * CHUNK_D);
lz++) {
for (int lx = std::max(pos.x, cx * CHUNK_W);
lx < std::min(pos.x + size.x, (cx + 1) * CHUNK_W);
lx++) {
uint idx = vox_index(
lx - pos.x, ly - pos.y, lz - pos.z, size.x, size.z
);
voxels[idx].id = BLOCK_VOID;
lights[idx] = 0;
}
}
}
}
int w = volume.getW(); static inline light_t apply_backlight(light_t light) {
int h = std::min<int>(volume.getH(), top); return Lightmap::combine(
int d = volume.getD(); std::min(15, Lightmap::extract(light, 0) + 1),
std::min(15, Lightmap::extract(light, 1) + 1),
std::min(15, Lightmap::extract(light, 2) + 1),
std::min(15, static_cast<int>(Lightmap::extract(light, 3)))
);
}
int scx = floordiv<CHUNK_W>(x); #include "util/timeutil.hpp"
int scz = floordiv<CHUNK_D>(z);
int ecx = floordiv<CHUNK_W>(x + w); // ugly
int ecz = floordiv<CHUNK_D>(z + d); static inline void sample_chunk(
const decltype(ContentIndices::blocks)& defs,
const Chunk& chunk,
voxel* voxels,
light_t* lights,
const glm::ivec3& pos,
const glm::ivec3& size,
int cx,
int cz,
bool backlight
) {
const auto cvoxels = chunk.voxels;
const auto clights = chunk.lightmap ? chunk.lightmap->getLights() : nullptr;
for (int ly = pos.y; ly < pos.y + size.y; ly++) {
for (int lz = std::max(pos.z, cz * CHUNK_D);
lz < std::min(pos.z + size.z, (cz + 1) * CHUNK_D);
lz++) {
for (int lx = std::max(pos.x, cx * CHUNK_W);
lx < std::min(pos.x + size.x, (cx + 1) * CHUNK_W);
lx++) {
uint vidx = vox_index(
lx - pos.x, ly - pos.y, lz - pos.z, size.x, size.z
);
uint cidx = vox_index(
lx - cx * CHUNK_W,
ly,
lz - cz * CHUNK_D,
CHUNK_W,
CHUNK_D
);
auto& vox = voxels[vidx];
vox = cvoxels[cidx];
light_t light = clights ? clights[cidx]
: Lightmap::SUN_LIGHT_ONLY;
// todo: move to the BlocksRenderer
if (backlight) {
const auto block = defs.get(vox.id);
if (block && block->lightPassing) {
light = apply_backlight(light);
}
}
lights[vidx] = light;
}
}
}
}
void Chunks::getVoxels(
voxel* voxels,
light_t* lights,
const glm::ivec3& pos,
const glm::ivec3& size,
bool backlight,
int top
) const {
timeutil::ScopeLogTimer log(111);
int h = std::min<int>(size.y, top);
int scx = floordiv<CHUNK_W>(pos.x);
int scz = floordiv<CHUNK_D>(pos.z);
int ecx = floordiv<CHUNK_W>(pos.x + size.x);
int ecz = floordiv<CHUNK_D>(pos.z + size.z);
int cw = ecx - scx + 1; int cw = ecx - scx + 1;
int cd = ecz - scz + 1; int cd = ecz - scz + 1;
@ -364,67 +444,37 @@ void Chunks::getVoxels(VoxelsVolume& volume, bool backlight, int top) const {
for (int cx = scx; cx < scx + cw; cx++) { for (int cx = scx; cx < scx + cw; cx++) {
const auto chunk = getChunk(cx, cz); const auto chunk = getChunk(cx, cz);
if (chunk == nullptr) { if (chunk == nullptr) {
// no chunk loaded -> filling with BLOCK_VOID fill_with_void(
for (int ly = y; ly < y + h; ly++) { voxels, lights, pos, {size.x, h, size.z}, cx, cz
for (int lz = std::max(z, cz * CHUNK_D); );
lz < std::min(z + d, (cz + 1) * CHUNK_D); continue;
lz++) {
for (int lx = std::max(x, cx * CHUNK_W);
lx < std::min(x + w, (cx + 1) * CHUNK_W);
lx++) {
uint idx = vox_index(lx - x, ly - y, lz - z, w, d);
voxels[idx].id = BLOCK_VOID;
lights[idx] = 0;
}
}
}
} else {
const voxel* cvoxels = chunk->voxels;
const light_t* clights =
chunk->lightmap ? chunk->lightmap->getLights() : nullptr;
for (int ly = y; ly < y + h; ly++) {
for (int lz = std::max(z, cz * CHUNK_D);
lz < std::min(z + d, (cz + 1) * CHUNK_D);
lz++) {
for (int lx = std::max(x, cx * CHUNK_W);
lx < std::min(x + w, (cx + 1) * CHUNK_W);
lx++) {
uint vidx = vox_index(lx - x, ly - y, lz - z, w, d);
uint cidx = vox_index(
lx - cx * CHUNK_W,
ly,
lz - cz * CHUNK_D,
CHUNK_W,
CHUNK_D
);
voxels[vidx] = cvoxels[cidx];
light_t light = clights ? clights[cidx]
: Lightmap::SUN_LIGHT_ONLY;
if (backlight) {
const auto block =
indices.blocks.get(voxels[vidx].id);
if (block && block->lightPassing) {
light = Lightmap::combine(
std::min(15,
Lightmap::extract(light, 0) + 1),
std::min(15,
Lightmap::extract(light, 1) + 1),
std::min(15,
Lightmap::extract(light, 2) + 1),
std::min(15,
static_cast<int>(Lightmap::extract(light, 3)))
);
}
}
lights[vidx] = light;
}
}
}
} }
sample_chunk(
indices.blocks,
*chunk,
voxels,
lights,
pos,
{size.x, h, size.z},
cx,
cz,
backlight
);
} }
} }
} }
void Chunks::getVoxels(VoxelsVolume& volume, bool backlight, int top) const {
getVoxels(
volume.getVoxels(),
volume.getLights(),
{volume.getX(), volume.getY(), volume.getZ()},
{volume.getW(), volume.getH(), volume.getD()},
backlight,
top
);
}
void Chunks::saveAndClear() { void Chunks::saveAndClear() {
areaMap.clear(); areaMap.clear();
} }

View file

@ -23,6 +23,8 @@ class LevelEvents;
class Block; class Block;
class VoxelsVolume; class VoxelsVolume;
template <int w, int h, int d> class StaticVoxelsVolume;
/// Player-centred chunks matrix /// Player-centred chunks matrix
class Chunks { class Chunks {
LevelEvents* events; LevelEvents* events;
@ -128,7 +130,34 @@ public:
bool isReplaceableBlock(int32_t x, int32_t y, int32_t z); bool isReplaceableBlock(int32_t x, int32_t y, int32_t z);
bool isObstacleBlock(int32_t x, int32_t y, int32_t z); bool isObstacleBlock(int32_t x, int32_t y, int32_t z);
void getVoxels(VoxelsVolume& volume, bool backlight = false, int top = CHUNK_H) const; void getVoxels(
VoxelsVolume& volume, bool backlight = false, int top = CHUNK_H
) const;
template <int w, int h, int d>
void getVoxels(
StaticVoxelsVolume<w, h, d>& volume,
bool backlight = false,
int top = CHUNK_H
) const {
getVoxels(
volume.getVoxels(),
volume.getLights(),
{volume.getX(), volume.getY(), volume.getZ()},
{w, h, d},
backlight,
top
);
}
void getVoxels(
voxel* voxels,
light_t* lights,
const glm::ivec3& pos,
const glm::ivec3& size,
bool backlight,
int top
) const;
void setCenter(int32_t x, int32_t z); void setCenter(int32_t x, int32_t z);
void resize(uint32_t newW, uint32_t newD); void resize(uint32_t newW, uint32_t newD);

View file

@ -1,28 +0,0 @@
#include "VoxelsVolume.hpp"
VoxelsVolume::VoxelsVolume(int x, int y, int z, int w, int h, int d)
: x(x),
y(y),
z(z),
w(w),
h(h),
d(d),
voxels(std::make_unique<voxel[]>(w * h * d)),
lights(std::make_unique<light_t[]>(w * h * d)) {
for (int i = 0; i < w * h * d; i++) {
voxels[i].id = BLOCK_VOID;
}
}
VoxelsVolume::VoxelsVolume(int w, int h, int d)
: VoxelsVolume(0, 0, 0, w, h, d) {
}
VoxelsVolume::~VoxelsVolume() {
}
void VoxelsVolume::setPosition(int x, int y, int z) {
this->x = x;
this->y = y;
this->z = z;
}

View file

@ -4,17 +4,25 @@
#include "typedefs.hpp" #include "typedefs.hpp"
#include "voxel.hpp" #include "voxel.hpp"
class VoxelsVolume { #include <cstring>
int x, y, z;
int w, h, d;
std::unique_ptr<voxel[]> voxels;
std::unique_ptr<light_t[]> lights;
public:
VoxelsVolume(int w, int h, int d);
VoxelsVolume(int x, int y, int z, int w, int h, int d);
virtual ~VoxelsVolume();
void setPosition(int x, int y, int z); class VoxelsVolume {
public:
static inline constexpr size_t MAX_VOXELS = CHUNK_VOL * 2;
VoxelsVolume(int w, int h, int d)
: VoxelsVolume(0, 0, 0, w, h, d) {}
VoxelsVolume(int x, int y, int z, int w, int h, int d)
: x(x), y(y), z(z), w(w), h(h), d(d) {
std::memset(voxels.get(), 0xFF, sizeof(voxel) * w * h * d);
}
void setPosition(int x, int y, int z) {
this->x = x;
this->y = y;
this->z = z;
}
int getX() const { int getX() const {
return x; return x;
@ -40,11 +48,11 @@ public:
return d; return d;
} }
voxel* getVoxels() const { voxel* getVoxels() {
return voxels.get(); return voxels.get();
} }
light_t* getLights() const { light_t* getLights() {
return lights.get(); return lights.get();
} }
@ -72,4 +80,83 @@ public:
} }
return lights[vox_index(bx - x, by - y, bz - z, w, d)]; return lights[vox_index(bx - x, by - y, bz - z, w, d)];
} }
private:
int x, y, z;
int w, h, d;
std::unique_ptr<voxel[]> voxels;
std::unique_ptr<light_t[]> lights;
}; };
template <int w, int h, int d>
class StaticVoxelsVolume {
public:
static inline constexpr size_t size = w * h * d;
static inline constexpr int width = w;
static inline constexpr int height = h;
static inline constexpr int depth = d;
StaticVoxelsVolume()
: StaticVoxelsVolume(0, 0, 0) {}
StaticVoxelsVolume(int x, int y, int z)
: x(x), y(y), z(z) {
std::memset(voxels, 0xFF, sizeof(voxel) * size);
}
void setPosition(int x, int y, int z) {
this->x = x;
this->y = y;
this->z = z;
}
int getX() const {
return x;
}
int getY() const {
return y;
}
int getZ() const {
return z;
}
voxel* getVoxels() {
return voxels;
}
light_t* getLights() {
return lights;
}
inline blockid_t pickBlockId(int bx, int by, int bz) const {
if (bx < x || by < y || bz < z || bx >= x + w || by >= y + h ||
bz >= z + d) {
return BLOCK_VOID;
}
return voxels[vox_index(bx - x, by - y, bz - z, w, d)].id;
}
inline voxel pickBlock(int bx, int by, int bz) const {
if (bx < x || by < y || bz < z || bx >= x + w || by >= y + h ||
bz >= z + d) {
return {BLOCK_VOID, {}};
}
return voxels[vox_index(bx - x, by - y, bz - z, w, d)];
}
inline light_t pickLight(int bx, int by, int bz) const {
if (bx < x || by < y || bz < z || bx >= x + w || by >= y + h ||
bz >= z + d) {
return 0;
}
return lights[vox_index(bx - x, by - y, bz - z, w, d)];
}
private:
int x, y, z;
voxel voxels[size];
light_t lights[size];
};

View file

@ -162,7 +162,7 @@ std::shared_ptr<Task> WorldConverter::startTask(
} }
auto pool = std::make_shared<util::ThreadPool<ConvertTask, int>>( auto pool = std::make_shared<util::ThreadPool<ConvertTask, int>>(
"converter-pool", "converter-pool",
[=]() { return std::make_shared<ConverterWorker>(converter); }, [=]() { return std::make_unique<ConverterWorker>(converter); },
[=](int&&) {} [=](int&&) {}
); );
auto& converterTasks = converter->tasks; auto& converterTasks = converter->tasks;