From 089e87da6919d3278846dd62353e27bb246a2df9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 24 Feb 2024 20:08:51 +0300 Subject: [PATCH] chunks rendering multithreading --- src/frontend/WorldRenderer.cpp | 9 +- .../graphics/BlocksRenderer.cpp | 937 +++++++++--------- src/{ => frontend}/graphics/BlocksRenderer.h | 10 +- src/frontend/graphics/ChunksRenderer.cpp | 150 +++ src/frontend/graphics/ChunksRenderer.h | 67 ++ src/graphics/ChunksRenderer.cpp | 51 - src/graphics/ChunksRenderer.h | 35 - src/util/stringutil.cpp | 20 + src/util/stringutil.h | 3 + src/voxels/WorldGenerator.h | 1 + 10 files changed, 726 insertions(+), 557 deletions(-) rename src/{ => frontend}/graphics/BlocksRenderer.cpp (94%) rename src/{ => frontend}/graphics/BlocksRenderer.h (90%) create mode 100644 src/frontend/graphics/ChunksRenderer.cpp create mode 100644 src/frontend/graphics/ChunksRenderer.h delete mode 100644 src/graphics/ChunksRenderer.cpp delete mode 100644 src/graphics/ChunksRenderer.h diff --git a/src/frontend/WorldRenderer.cpp b/src/frontend/WorldRenderer.cpp index 6e7daa88..e83e1ed8 100644 --- a/src/frontend/WorldRenderer.cpp +++ b/src/frontend/WorldRenderer.cpp @@ -8,7 +8,6 @@ #include "../window/Window.h" #include "../window/Camera.h" #include "../content/Content.h" -#include "../graphics/ChunksRenderer.h" #include "../graphics/Mesh.h" #include "../graphics/Atlas.h" #include "../graphics/Shader.h" @@ -33,6 +32,7 @@ #include "../items/Inventory.h" #include "LevelFrontend.h" #include "graphics/Skybox.h" +#include "graphics/ChunksRenderer.h" using glm::vec3; using glm::vec4; @@ -74,7 +74,11 @@ bool WorldRenderer::drawChunk(size_t index, if (!chunk->isLighted()) { return false; } - auto mesh = renderer->getOrRender(chunk.get()); + float distance = glm::distance( + camera->position, + glm::vec3((chunk->x + 0.5f) * CHUNK_W, camera->position.y, (chunk->z + 0.5f) * CHUNK_D) + ); + auto mesh = renderer->getOrRender(chunk, distance < CHUNK_W*1.5f); if (mesh == nullptr) { return false; } @@ -98,6 +102,7 @@ bool WorldRenderer::drawChunk(size_t index, void WorldRenderer::drawChunks(Chunks* chunks, Camera* camera, Shader* shader) { + renderer->update(); std::vector indices; for (size_t i = 0; i < chunks->volume; i++){ if (chunks->chunks[i] == nullptr) diff --git a/src/graphics/BlocksRenderer.cpp b/src/frontend/graphics/BlocksRenderer.cpp similarity index 94% rename from src/graphics/BlocksRenderer.cpp rename to src/frontend/graphics/BlocksRenderer.cpp index bd9069cf..85d5f337 100644 --- a/src/graphics/BlocksRenderer.cpp +++ b/src/frontend/graphics/BlocksRenderer.cpp @@ -1,465 +1,472 @@ -#include "BlocksRenderer.h" - -#include - -#include "Mesh.h" -#include "UVRegion.h" -#include "../constants.h" -#include "../content/Content.h" -#include "../voxels/Block.h" -#include "../voxels/Chunk.h" -#include "../voxels/VoxelsVolume.h" -#include "../voxels/ChunksStorage.h" -#include "../lighting/Lightmap.h" -#include "../frontend/ContentGfxCache.h" - -using glm::ivec3; -using glm::vec3; -using glm::vec4; - -const uint BlocksRenderer::VERTEX_SIZE = 6; -const vec3 BlocksRenderer::SUN_VECTOR (0.411934f, 0.863868f, -0.279161f); - -BlocksRenderer::BlocksRenderer(size_t capacity, - const Content* content, - const ContentGfxCache* cache, - const EngineSettings& settings) - : content(content), - vertexOffset(0), - indexOffset(0), - indexSize(0), - capacity(capacity), - cache(cache), - settings(settings) { - vertexBuffer = new float[capacity]; - indexBuffer = new int[capacity]; - voxelsBuffer = new VoxelsVolume(CHUNK_W + 2, CHUNK_H, CHUNK_D + 2); - blockDefsCache = content->getIndices()->getBlockDefs(); -} - -BlocksRenderer::~BlocksRenderer() { - delete voxelsBuffer; - delete[] vertexBuffer; - delete[] indexBuffer; -} - -/* Basic vertex add method */ -void BlocksRenderer::vertex(const vec3& coord, float u, float v, const vec4& light) { - vertexBuffer[vertexOffset++] = coord.x; - vertexBuffer[vertexOffset++] = coord.y; - vertexBuffer[vertexOffset++] = coord.z; - - vertexBuffer[vertexOffset++] = u; - vertexBuffer[vertexOffset++] = v; - - union { - float floating; - uint32_t integer; - } compressed; - - compressed.integer = (uint32_t(light.r * 255) & 0xff) << 24; - compressed.integer |= (uint32_t(light.g * 255) & 0xff) << 16; - compressed.integer |= (uint32_t(light.b * 255) & 0xff) << 8; - compressed.integer |= (uint32_t(light.a * 255) & 0xff); - - vertexBuffer[vertexOffset++] = compressed.floating; -} - -void BlocksRenderer::index(int a, int b, int c, int d, int e, int f) { - indexBuffer[indexSize++] = indexOffset + a; - indexBuffer[indexSize++] = indexOffset + b; - indexBuffer[indexSize++] = indexOffset + c; - indexBuffer[indexSize++] = indexOffset + d; - indexBuffer[indexSize++] = indexOffset + e; - indexBuffer[indexSize++] = indexOffset + f; - indexOffset += 4; -} - -/* Add face with precalculated lights */ -void BlocksRenderer::face(const vec3& coord, - float w, float h, float d, - const vec3& axisX, - const vec3& axisY, - const vec3& axisZ, - const UVRegion& region, - const vec4(&lights)[4], - const vec4& tint) { - if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) { - overflow = true; - return; - } - vec3 X = axisX * w; - vec3 Y = axisY * h; - vec3 Z = axisZ * d; - float s = 0.5f; - vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, lights[0] * tint); - vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, lights[1] * tint); - vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, lights[2] * tint); - vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, lights[3] * tint); - index(0, 1, 3, 1, 2, 3); -} - -void BlocksRenderer::vertex(const vec3& coord, - float u, float v, - const vec4& tint, - const vec3& axisX, - const vec3& axisY, - const vec3& axisZ) { - vec3 pos = coord+axisZ*0.5f+(axisX+axisY)*0.5f; - vec4 light = pickSoftLight(ivec3(round(pos.x), round(pos.y), round(pos.z)), axisX, axisY); - vertex(coord, u, v, light * tint); -} - -void BlocksRenderer::face(const vec3& coord, - const vec3& X, - const vec3& Y, - const vec3& Z, - const UVRegion& region, - bool lights) { - if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) { - overflow = true; - return; - } - - float s = 0.5f; - if (lights) { - float d = glm::dot(Z, SUN_VECTOR); - d = 0.8f + d * 0.2f; - - vec3 axisX = glm::normalize(X); - vec3 axisY = glm::normalize(Y); - vec3 axisZ = glm::normalize(Z); - - vec4 tint(d); - vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, tint, axisX, axisY, axisZ); - vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, tint, axisX, axisY, axisZ); - vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, tint, axisX, axisY, axisZ); - vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, tint, axisX, axisY, axisZ); - } else { - vec4 tint(1.0f); - vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, tint); - vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, tint); - vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, tint); - vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, tint); - } - index(0, 1, 2, 0, 2, 3); -} - -void BlocksRenderer::tetragonicFace(const vec3& coord, const vec3& p1, - const vec3& p2, const vec3& p3, const vec3& p4, - const vec3& X, - const vec3& Y, - const vec3& Z, - const UVRegion& texreg, - bool lights) { - - const vec3 fp1 = (p1.x - 0.5f) * X + (p1.y - 0.5f) * Y + (p1.z - 0.5f) * Z; - const vec3 fp2 = (p2.x - 0.5f) * X + (p2.y - 0.5f) * Y + (p2.z - 0.5f) * Z; - const vec3 fp3 = (p3.x - 0.5f) * X + (p3.y - 0.5f) * Y + (p3.z - 0.5f) * Z; - const vec3 fp4 = (p4.x - 0.5f) * X + (p4.y - 0.5f) * Y + (p4.z - 0.5f) * Z; - - vec4 tint(1.0f); - if (lights) { - vec3 dir = glm::cross(fp2 - fp1, fp3 - fp1); - vec3 normal = glm::normalize(dir); - - float d = glm::dot(normal, SUN_VECTOR); - d = 0.8f + d * 0.2f; - tint *= d; - tint *= pickLight(coord); - // debug normal - // tint.x = normal.x * 0.5f + 0.5f; - // tint.y = normal.y * 0.5f + 0.5f; - // tint.z = normal.z * 0.5f + 0.5f; - } - vertex(coord + fp1, texreg.u1, texreg.v1, tint); - vertex(coord + fp2, texreg.u2, texreg.v1, tint); - vertex(coord + fp3, texreg.u2, texreg.v2, tint); - vertex(coord + fp4, texreg.u1, texreg.v2, tint); - index(0, 1, 3, 1, 2, 3); -} - -void BlocksRenderer::blockXSprite(int x, int y, int z, - const vec3& size, - const UVRegion& texface1, - const UVRegion& texface2, - float spread) { - vec4 lights[]{ - pickSoftLight({x, y + 1, z}, {1, 0, 0}, {0, 1, 0}), - pickSoftLight({x + 1, y + 1, z}, {1, 0, 0}, {0, 1, 0}), - pickSoftLight({x + 1, y + 1, z}, {1, 0, 0}, {0, 1, 0}), - pickSoftLight({x, y + 1, z}, {1, 0, 0}, {0, 1, 0}) }; - - int rand = ((x * z + y) ^ (z * y - x)) * (z + y); - - float xs = ((float)(char)rand / 512) * spread; - float zs = ((float)(char)(rand >> 8) / 512) * spread; - - const float w = size.x / 1.41f; - const float tint = 0.8f; - - face(vec3(x + xs, y, z + zs), - w, size.y, 0, vec3(1, 0, 1), vec3(0, 1, 0), vec3(), - texface1, lights, vec4(tint)); - face(vec3(x + xs, y, z + zs), - w, size.y, 0, vec3(-1, 0, -1), vec3(0, 1, 0), vec3(), - texface1, lights, vec4(tint)); - - face(vec3(x + xs, y, z + zs), - w, size.y, 0, vec3(1, 0, -1), vec3(0, 1, 0), vec3(), - texface1, lights, vec4(tint)); - face(vec3(x + xs, y, z + zs), - w, size.y, 0, vec3(-1, 0, 1), vec3(0, 1, 0), vec3(), - texface1, lights, vec4(tint)); -} - -// HINT: texture faces order: {east, west, bottom, top, south, north} - -/* AABB blocks render method */ -void BlocksRenderer::blockAABB(const ivec3& icoord, - const UVRegion(&texfaces)[6], - const Block* block, ubyte rotation, - bool lights) { - if (block->hitboxes.empty()) { - return; - } - - AABB hitbox = block->hitboxes[0]; - for (const auto& box : block->hitboxes) { - hitbox.a = glm::min(hitbox.a, box.a); - hitbox.b = glm::max(hitbox.b, box.b); - } - - vec3 size = hitbox.size(); - vec3 X(1, 0, 0); - vec3 Y(0, 1, 0); - vec3 Z(0, 0, 1); - vec3 coord(icoord); - if (block->rotatable) { - auto& rotations = block->rotations; - auto& orient = rotations.variants[rotation]; - X = orient.axisX; - Y = orient.axisY; - Z = orient.axisZ; - orient.transform(hitbox); - } - - coord = vec3(icoord) - vec3(0.5f) + hitbox.center(); - - face(coord, X*size.x, Y*size.y, Z*size.z, texfaces[5], lights); // north - face(coord, -X*size.x, Y*size.y, -Z*size.z, texfaces[4], lights); // south - - face(coord, X*size.x, -Z*size.z, Y*size.y, texfaces[3], lights); // top - face(coord, -X*size.x, -Z*size.z, -Y*size.y, texfaces[2], lights); // bottom - - face(coord, -Z*size.z, Y*size.y, X*size.x, texfaces[1], lights); // west - face(coord, Z*size.z, Y*size.y, -X*size.x, texfaces[0], lights); // east -} - -void BlocksRenderer::blockCustomModel(const ivec3& icoord, - const Block* block, ubyte rotation, bool lights) { - vec3 X(1, 0, 0); - vec3 Y(0, 1, 0); - vec3 Z(0, 0, 1); - CoordSystem orient(X,Y,Z); - vec3 coord(icoord); - if (block->rotatable) { - auto& rotations = block->rotations; - orient = rotations.variants[rotation]; - X = orient.axisX; - Y = orient.axisY; - Z = orient.axisZ; - } - - for (size_t i = 0; i < block->modelBoxes.size(); i++) { - AABB box = block->modelBoxes[i]; - vec3 size = box.size(); - if (block->rotatable) { - orient.transform(box); - } - vec3 center_coord = coord - vec3(0.5f) + box.center(); - face(center_coord, X * size.x, Y * size.y, Z * size.z, block->modelUVs[i * 6 + 5], lights); // north - face(center_coord, -X * size.x, Y * size.y, -Z * size.z, block->modelUVs[i * 6 + 4], lights); // south - face(center_coord, X * size.x, -Z * size.z, Y * size.y, block->modelUVs[i * 6 + 3], lights); // top - face(center_coord, -X * size.x, -Z * size.z, -Y * size.y, block->modelUVs[i * 6 + 2], lights); // bottom - face(center_coord, -Z * size.z, Y * size.y, X * size.x, block->modelUVs[i * 6 + 1], lights); // west - face(center_coord, Z * size.z, Y * size.y, -X * size.x, block->modelUVs[i * 6 + 0], lights); // east - } - - for (size_t i = 0; i < block->modelExtraPoints.size()/4; i++) { - tetragonicFace(coord, - block->modelExtraPoints[i * 4 + 0], - block->modelExtraPoints[i * 4 + 1], - block->modelExtraPoints[i * 4 + 2], - block->modelExtraPoints[i * 4 + 3], - X, Y, Z, - block->modelUVs[block->modelBoxes.size()*6 + i], lights); - } -} - -/* Fastest solid shaded blocks render method */ -void BlocksRenderer::blockCube(int x, int y, int z, - const UVRegion(&texfaces)[6], - const Block* block, - ubyte states, - bool lights) { - ubyte group = block->drawGroup; - - vec3 X(1, 0, 0); - vec3 Y(0, 1, 0); - vec3 Z(0, 0, 1); - vec3 coord(x, y, z); - if (block->rotatable) { - auto& rotations = block->rotations; - auto& orient = rotations.variants[states & BLOCK_ROT_MASK]; - X = orient.axisX; - Y = orient.axisY; - Z = orient.axisZ; - } - - if (isOpen(x+Z.x, y+Z.y, z+Z.z, group)) { - face(coord, X, Y, Z, texfaces[5], lights); - } - if (isOpen(x-Z.x, y-Z.y, z-Z.z, group)) { - face(coord, -X, Y, -Z, texfaces[4], lights); - } - if (isOpen(x+Y.x, y+Y.y, z+Y.z, group)) { - face(coord, X, -Z, Y, texfaces[3], lights); - } - if (isOpen(x-Y.x, y-Y.y, z-Y.z, group)) { - face(coord, X, Z, -Y, texfaces[2], lights); - } - if (isOpen(x+X.x, y+X.y, z+X.z, group)) { - face(coord, -Z, Y, X, texfaces[1], lights); - } - if (isOpen(x-X.x, y-X.y, z-X.z, group)) { - face(coord, Z, Y, -X, texfaces[0], lights); - } -} - -// Does block allow to see other blocks sides (is it transparent) -bool BlocksRenderer::isOpen(int x, int y, int z, ubyte group) 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.drawGroup != group && block.lightPassing) || !block.rt.solid) { - return true; - } - return !id; -} - -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; -} - -vec4 BlocksRenderer::pickLight(int x, int y, int z) const { - if (isOpenForLight(x, y, z)) { - light_t light = voxelsBuffer->pickLight(chunk->x * CHUNK_W + x, - y, - chunk->z * CHUNK_D + z); - return vec4(Lightmap::extract(light, 0) / 15.0f, - Lightmap::extract(light, 1) / 15.0f, - Lightmap::extract(light, 2) / 15.0f, - Lightmap::extract(light, 3) / 15.0f); - } - else { - return vec4(0.0f); - } -} - -vec4 BlocksRenderer::pickLight(const ivec3& coord) const { - return pickLight(coord.x, coord.y, coord.z); -} - -vec4 BlocksRenderer::pickSoftLight(const ivec3& coord, - const ivec3& right, - const ivec3& up) const { - return ( - pickLight(coord) + - pickLight(coord - right) + - pickLight(coord - right - up) + - pickLight(coord - up)) * 0.25f; -} - -vec4 BlocksRenderer::pickSoftLight(float x, float y, float z, - const ivec3& right, - const ivec3& up) const { - return pickSoftLight({int(round(x)), int(round(y)), int(round(z))}, right, up); -} - -void BlocksRenderer::render(const voxel* voxels) { - int begin = chunk->bottom * (CHUNK_W * CHUNK_D); - int end = chunk->top * (CHUNK_W * CHUNK_D); - for (const auto drawGroup : *content->drawGroups) { - for (int i = begin; i < end; i++) { - const voxel& vox = voxels[i]; - blockid_t id = vox.id; - const Block& def = *blockDefsCache[id]; - if (id == 0 || def.drawGroup != drawGroup) - continue; - const UVRegion texfaces[6]{ cache->getRegion(id, 0), - cache->getRegion(id, 1), - cache->getRegion(id, 2), - cache->getRegion(id, 3), - cache->getRegion(id, 4), - cache->getRegion(id, 5)}; - int x = i % CHUNK_W; - int y = i / (CHUNK_D * CHUNK_W); - int z = (i / CHUNK_D) % CHUNK_W; - switch (def.model) { - case BlockModel::block: - blockCube(x, y, z, texfaces, &def, vox.states, !def.rt.emissive); - break; - case BlockModel::xsprite: { - blockXSprite(x, y, z, vec3(1.0f), - texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f); - break; - } - case BlockModel::aabb: { - blockAABB(ivec3(x,y,z), texfaces, &def, vox.rotation(), !def.rt.emissive); - break; - } - case BlockModel::custom: { - blockCustomModel(ivec3(x, y, z), &def, vox.rotation(), !def.rt.emissive); - break; - } - default: - break; - } - if (overflow) - return; - } - } -} - -Mesh* BlocksRenderer::render(const Chunk* chunk, const ChunksStorage* chunks) { - this->chunk = chunk; - voxelsBuffer->setPosition(chunk->x * CHUNK_W - 1, 0, chunk->z * CHUNK_D - 1); - chunks->getVoxels(voxelsBuffer, settings.graphics.backlight); - overflow = false; - vertexOffset = 0; - indexOffset = indexSize = 0; - const voxel* voxels = chunk->voxels; - render(voxels); - - const vattr attrs[]{ {3}, {2}, {1}, {0} }; - size_t vcount = vertexOffset / BlocksRenderer::VERTEX_SIZE; - Mesh* mesh = new Mesh(vertexBuffer, vcount, indexBuffer, indexSize, attrs); - return mesh; -} - -VoxelsVolume* BlocksRenderer::getVoxelsBuffer() const { - return voxelsBuffer; -} +#include "BlocksRenderer.h" + +#include + +#include "../../graphics/Mesh.h" +#include "../../graphics/UVRegion.h" +#include "../../constants.h" +#include "../../content/Content.h" +#include "../../voxels/Block.h" +#include "../../voxels/Chunk.h" +#include "../../voxels/VoxelsVolume.h" +#include "../../voxels/ChunksStorage.h" +#include "../../lighting/Lightmap.h" +#include "../../frontend/ContentGfxCache.h" + +using glm::ivec3; +using glm::vec3; +using glm::vec4; + +const uint BlocksRenderer::VERTEX_SIZE = 6; +const vec3 BlocksRenderer::SUN_VECTOR (0.411934f, 0.863868f, -0.279161f); + +BlocksRenderer::BlocksRenderer(size_t capacity, + const Content* content, + const ContentGfxCache* cache, + const EngineSettings& settings) + : content(content), + vertexOffset(0), + indexOffset(0), + indexSize(0), + capacity(capacity), + cache(cache), + settings(settings) { + vertexBuffer = new float[capacity]; + indexBuffer = new int[capacity]; + voxelsBuffer = new VoxelsVolume(CHUNK_W + 2, CHUNK_H, CHUNK_D + 2); + blockDefsCache = content->getIndices()->getBlockDefs(); +} + +BlocksRenderer::~BlocksRenderer() { + delete voxelsBuffer; + delete[] vertexBuffer; + delete[] indexBuffer; +} + +/* Basic vertex add method */ +void BlocksRenderer::vertex(const vec3& coord, float u, float v, const vec4& light) { + vertexBuffer[vertexOffset++] = coord.x; + vertexBuffer[vertexOffset++] = coord.y; + vertexBuffer[vertexOffset++] = coord.z; + + vertexBuffer[vertexOffset++] = u; + vertexBuffer[vertexOffset++] = v; + + union { + float floating; + uint32_t integer; + } compressed; + + compressed.integer = (uint32_t(light.r * 255) & 0xff) << 24; + compressed.integer |= (uint32_t(light.g * 255) & 0xff) << 16; + compressed.integer |= (uint32_t(light.b * 255) & 0xff) << 8; + compressed.integer |= (uint32_t(light.a * 255) & 0xff); + + vertexBuffer[vertexOffset++] = compressed.floating; +} + +void BlocksRenderer::index(int a, int b, int c, int d, int e, int f) { + indexBuffer[indexSize++] = indexOffset + a; + indexBuffer[indexSize++] = indexOffset + b; + indexBuffer[indexSize++] = indexOffset + c; + indexBuffer[indexSize++] = indexOffset + d; + indexBuffer[indexSize++] = indexOffset + e; + indexBuffer[indexSize++] = indexOffset + f; + indexOffset += 4; +} + +/* Add face with precalculated lights */ +void BlocksRenderer::face(const vec3& coord, + float w, float h, float d, + const vec3& axisX, + const vec3& axisY, + const vec3& axisZ, + const UVRegion& region, + const vec4(&lights)[4], + const vec4& tint) { + if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) { + overflow = true; + return; + } + vec3 X = axisX * w; + vec3 Y = axisY * h; + vec3 Z = axisZ * d; + float s = 0.5f; + vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, lights[0] * tint); + vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, lights[1] * tint); + vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, lights[2] * tint); + vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, lights[3] * tint); + index(0, 1, 3, 1, 2, 3); +} + +void BlocksRenderer::vertex(const vec3& coord, + float u, float v, + const vec4& tint, + const vec3& axisX, + const vec3& axisY, + const vec3& axisZ) { + vec3 pos = coord+axisZ*0.5f+(axisX+axisY)*0.5f; + vec4 light = pickSoftLight(ivec3(round(pos.x), round(pos.y), round(pos.z)), axisX, axisY); + vertex(coord, u, v, light * tint); +} + +void BlocksRenderer::face(const vec3& coord, + const vec3& X, + const vec3& Y, + const vec3& Z, + const UVRegion& region, + bool lights) { + if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) { + overflow = true; + return; + } + + float s = 0.5f; + if (lights) { + float d = glm::dot(Z, SUN_VECTOR); + d = 0.8f + d * 0.2f; + + vec3 axisX = glm::normalize(X); + vec3 axisY = glm::normalize(Y); + vec3 axisZ = glm::normalize(Z); + + vec4 tint(d); + vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, tint, axisX, axisY, axisZ); + vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, tint, axisX, axisY, axisZ); + vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, tint, axisX, axisY, axisZ); + vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, tint, axisX, axisY, axisZ); + } else { + vec4 tint(1.0f); + vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, tint); + vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, tint); + vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, tint); + vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, tint); + } + index(0, 1, 2, 0, 2, 3); +} + +void BlocksRenderer::tetragonicFace(const vec3& coord, const vec3& p1, + const vec3& p2, const vec3& p3, const vec3& p4, + const vec3& X, + const vec3& Y, + const vec3& Z, + const UVRegion& texreg, + bool lights) { + + const vec3 fp1 = (p1.x - 0.5f) * X + (p1.y - 0.5f) * Y + (p1.z - 0.5f) * Z; + const vec3 fp2 = (p2.x - 0.5f) * X + (p2.y - 0.5f) * Y + (p2.z - 0.5f) * Z; + const vec3 fp3 = (p3.x - 0.5f) * X + (p3.y - 0.5f) * Y + (p3.z - 0.5f) * Z; + const vec3 fp4 = (p4.x - 0.5f) * X + (p4.y - 0.5f) * Y + (p4.z - 0.5f) * Z; + + vec4 tint(1.0f); + if (lights) { + vec3 dir = glm::cross(fp2 - fp1, fp3 - fp1); + vec3 normal = glm::normalize(dir); + + float d = glm::dot(normal, SUN_VECTOR); + d = 0.8f + d * 0.2f; + tint *= d; + tint *= pickLight(coord); + // debug normal + // tint.x = normal.x * 0.5f + 0.5f; + // tint.y = normal.y * 0.5f + 0.5f; + // tint.z = normal.z * 0.5f + 0.5f; + } + vertex(coord + fp1, texreg.u1, texreg.v1, tint); + vertex(coord + fp2, texreg.u2, texreg.v1, tint); + vertex(coord + fp3, texreg.u2, texreg.v2, tint); + vertex(coord + fp4, texreg.u1, texreg.v2, tint); + index(0, 1, 3, 1, 2, 3); +} + +void BlocksRenderer::blockXSprite(int x, int y, int z, + const vec3& size, + const UVRegion& texface1, + const UVRegion& texface2, + float spread) { + vec4 lights[]{ + pickSoftLight({x, y + 1, z}, {1, 0, 0}, {0, 1, 0}), + pickSoftLight({x + 1, y + 1, z}, {1, 0, 0}, {0, 1, 0}), + pickSoftLight({x + 1, y + 1, z}, {1, 0, 0}, {0, 1, 0}), + pickSoftLight({x, y + 1, z}, {1, 0, 0}, {0, 1, 0}) }; + + int rand = ((x * z + y) ^ (z * y - x)) * (z + y); + + float xs = ((float)(char)rand / 512) * spread; + float zs = ((float)(char)(rand >> 8) / 512) * spread; + + const float w = size.x / 1.41f; + const float tint = 0.8f; + + face(vec3(x + xs, y, z + zs), + w, size.y, 0, vec3(1, 0, 1), vec3(0, 1, 0), vec3(), + texface1, lights, vec4(tint)); + face(vec3(x + xs, y, z + zs), + w, size.y, 0, vec3(-1, 0, -1), vec3(0, 1, 0), vec3(), + texface1, lights, vec4(tint)); + + face(vec3(x + xs, y, z + zs), + w, size.y, 0, vec3(1, 0, -1), vec3(0, 1, 0), vec3(), + texface1, lights, vec4(tint)); + face(vec3(x + xs, y, z + zs), + w, size.y, 0, vec3(-1, 0, 1), vec3(0, 1, 0), vec3(), + texface1, lights, vec4(tint)); +} + +// HINT: texture faces order: {east, west, bottom, top, south, north} + +/* AABB blocks render method */ +void BlocksRenderer::blockAABB(const ivec3& icoord, + const UVRegion(&texfaces)[6], + const Block* block, ubyte rotation, + bool lights) { + if (block->hitboxes.empty()) { + return; + } + + AABB hitbox = block->hitboxes[0]; + for (const auto& box : block->hitboxes) { + hitbox.a = glm::min(hitbox.a, box.a); + hitbox.b = glm::max(hitbox.b, box.b); + } + + vec3 size = hitbox.size(); + vec3 X(1, 0, 0); + vec3 Y(0, 1, 0); + vec3 Z(0, 0, 1); + vec3 coord(icoord); + if (block->rotatable) { + auto& rotations = block->rotations; + auto& orient = rotations.variants[rotation]; + X = orient.axisX; + Y = orient.axisY; + Z = orient.axisZ; + orient.transform(hitbox); + } + + coord = vec3(icoord) - vec3(0.5f) + hitbox.center(); + + face(coord, X*size.x, Y*size.y, Z*size.z, texfaces[5], lights); // north + face(coord, -X*size.x, Y*size.y, -Z*size.z, texfaces[4], lights); // south + + face(coord, X*size.x, -Z*size.z, Y*size.y, texfaces[3], lights); // top + face(coord, -X*size.x, -Z*size.z, -Y*size.y, texfaces[2], lights); // bottom + + face(coord, -Z*size.z, Y*size.y, X*size.x, texfaces[1], lights); // west + face(coord, Z*size.z, Y*size.y, -X*size.x, texfaces[0], lights); // east +} + +void BlocksRenderer::blockCustomModel(const ivec3& icoord, + const Block* block, ubyte rotation, bool lights) { + vec3 X(1, 0, 0); + vec3 Y(0, 1, 0); + vec3 Z(0, 0, 1); + CoordSystem orient(X,Y,Z); + vec3 coord(icoord); + if (block->rotatable) { + auto& rotations = block->rotations; + orient = rotations.variants[rotation]; + X = orient.axisX; + Y = orient.axisY; + Z = orient.axisZ; + } + + for (size_t i = 0; i < block->modelBoxes.size(); i++) { + AABB box = block->modelBoxes[i]; + vec3 size = box.size(); + if (block->rotatable) { + orient.transform(box); + } + vec3 center_coord = coord - vec3(0.5f) + box.center(); + face(center_coord, X * size.x, Y * size.y, Z * size.z, block->modelUVs[i * 6 + 5], lights); // north + face(center_coord, -X * size.x, Y * size.y, -Z * size.z, block->modelUVs[i * 6 + 4], lights); // south + face(center_coord, X * size.x, -Z * size.z, Y * size.y, block->modelUVs[i * 6 + 3], lights); // top + face(center_coord, -X * size.x, -Z * size.z, -Y * size.y, block->modelUVs[i * 6 + 2], lights); // bottom + face(center_coord, -Z * size.z, Y * size.y, X * size.x, block->modelUVs[i * 6 + 1], lights); // west + face(center_coord, Z * size.z, Y * size.y, -X * size.x, block->modelUVs[i * 6 + 0], lights); // east + } + + for (size_t i = 0; i < block->modelExtraPoints.size()/4; i++) { + tetragonicFace(coord, + block->modelExtraPoints[i * 4 + 0], + block->modelExtraPoints[i * 4 + 1], + block->modelExtraPoints[i * 4 + 2], + block->modelExtraPoints[i * 4 + 3], + X, Y, Z, + block->modelUVs[block->modelBoxes.size()*6 + i], lights); + } +} + +/* Fastest solid shaded blocks render method */ +void BlocksRenderer::blockCube(int x, int y, int z, + const UVRegion(&texfaces)[6], + const Block* block, + ubyte states, + bool lights) { + ubyte group = block->drawGroup; + + vec3 X(1, 0, 0); + vec3 Y(0, 1, 0); + vec3 Z(0, 0, 1); + vec3 coord(x, y, z); + if (block->rotatable) { + auto& rotations = block->rotations; + auto& orient = rotations.variants[states & BLOCK_ROT_MASK]; + X = orient.axisX; + Y = orient.axisY; + Z = orient.axisZ; + } + + if (isOpen(x+Z.x, y+Z.y, z+Z.z, group)) { + face(coord, X, Y, Z, texfaces[5], lights); + } + if (isOpen(x-Z.x, y-Z.y, z-Z.z, group)) { + face(coord, -X, Y, -Z, texfaces[4], lights); + } + if (isOpen(x+Y.x, y+Y.y, z+Y.z, group)) { + face(coord, X, -Z, Y, texfaces[3], lights); + } + if (isOpen(x-Y.x, y-Y.y, z-Y.z, group)) { + face(coord, X, Z, -Y, texfaces[2], lights); + } + if (isOpen(x+X.x, y+X.y, z+X.z, group)) { + face(coord, -Z, Y, X, texfaces[1], lights); + } + if (isOpen(x-X.x, y-X.y, z-X.z, group)) { + face(coord, Z, Y, -X, texfaces[0], lights); + } +} + +// Does block allow to see other blocks sides (is it transparent) +bool BlocksRenderer::isOpen(int x, int y, int z, ubyte group) 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.drawGroup != group && block.lightPassing) || !block.rt.solid) { + return true; + } + return !id; +} + +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; +} + +vec4 BlocksRenderer::pickLight(int x, int y, int z) const { + if (isOpenForLight(x, y, z)) { + light_t light = voxelsBuffer->pickLight(chunk->x * CHUNK_W + x, + y, + chunk->z * CHUNK_D + z); + return vec4(Lightmap::extract(light, 0) / 15.0f, + Lightmap::extract(light, 1) / 15.0f, + Lightmap::extract(light, 2) / 15.0f, + Lightmap::extract(light, 3) / 15.0f); + } + else { + return vec4(0.0f); + } +} + +vec4 BlocksRenderer::pickLight(const ivec3& coord) const { + return pickLight(coord.x, coord.y, coord.z); +} + +vec4 BlocksRenderer::pickSoftLight(const ivec3& coord, + const ivec3& right, + const ivec3& up) const { + return ( + pickLight(coord) + + pickLight(coord - right) + + pickLight(coord - right - up) + + pickLight(coord - up)) * 0.25f; +} + +vec4 BlocksRenderer::pickSoftLight(float x, float y, float z, + const ivec3& right, + const ivec3& up) const { + return pickSoftLight({int(round(x)), int(round(y)), int(round(z))}, right, up); +} + +void BlocksRenderer::render(const voxel* voxels) { + int begin = chunk->bottom * (CHUNK_W * CHUNK_D); + int end = chunk->top * (CHUNK_W * CHUNK_D); + for (const auto drawGroup : *content->drawGroups) { + for (int i = begin; i < end; i++) { + const voxel& vox = voxels[i]; + blockid_t id = vox.id; + const Block& def = *blockDefsCache[id]; + if (id == 0 || def.drawGroup != drawGroup) + continue; + const UVRegion texfaces[6]{ cache->getRegion(id, 0), + cache->getRegion(id, 1), + cache->getRegion(id, 2), + cache->getRegion(id, 3), + cache->getRegion(id, 4), + cache->getRegion(id, 5)}; + int x = i % CHUNK_W; + int y = i / (CHUNK_D * CHUNK_W); + int z = (i / CHUNK_D) % CHUNK_W; + switch (def.model) { + case BlockModel::block: + blockCube(x, y, z, texfaces, &def, vox.states, !def.rt.emissive); + break; + case BlockModel::xsprite: { + blockXSprite(x, y, z, vec3(1.0f), + texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f); + break; + } + case BlockModel::aabb: { + blockAABB(ivec3(x,y,z), texfaces, &def, vox.rotation(), !def.rt.emissive); + break; + } + case BlockModel::custom: { + blockCustomModel(ivec3(x, y, z), &def, vox.rotation(), !def.rt.emissive); + break; + } + default: + break; + } + if (overflow) + return; + } + } +} + +void BlocksRenderer::build(const Chunk* chunk, const ChunksStorage* chunks) { + this->chunk = chunk; + voxelsBuffer->setPosition(chunk->x * CHUNK_W - 1, 0, chunk->z * CHUNK_D - 1); + chunks->getVoxels(voxelsBuffer, settings.graphics.backlight); + overflow = false; + vertexOffset = 0; + indexOffset = indexSize = 0; + const voxel* voxels = chunk->voxels; + render(voxels); +} + +Mesh* BlocksRenderer::createMesh() { + const vattr attrs[]{ {3}, {2}, {1}, {0} }; + size_t vcount = vertexOffset / BlocksRenderer::VERTEX_SIZE; + Mesh* mesh = new Mesh(vertexBuffer, vcount, indexBuffer, indexSize, attrs); + return mesh; +} + +Mesh* BlocksRenderer::render(const Chunk* chunk, const ChunksStorage* chunks) { + build(chunk, chunks); + return createMesh(); +} + +VoxelsVolume* BlocksRenderer::getVoxelsBuffer() const { + return voxelsBuffer; +} \ No newline at end of file diff --git a/src/graphics/BlocksRenderer.h b/src/frontend/graphics/BlocksRenderer.h similarity index 90% rename from src/graphics/BlocksRenderer.h rename to src/frontend/graphics/BlocksRenderer.h index 02d89de3..229fd70e 100644 --- a/src/graphics/BlocksRenderer.h +++ b/src/frontend/graphics/BlocksRenderer.h @@ -4,10 +4,10 @@ #include #include #include -#include "UVRegion.h" -#include "../typedefs.h" -#include "../voxels/voxel.h" -#include "../settings.h" +#include "../../graphics/UVRegion.h" +#include "../../voxels/voxel.h" +#include "../../typedefs.h" +#include "../../settings.h" class Content; class Mesh; @@ -93,7 +93,9 @@ public: BlocksRenderer(size_t capacity, const Content* content, const ContentGfxCache* cache, const EngineSettings& settings); virtual ~BlocksRenderer(); + void build(const Chunk* chunk, const ChunksStorage* chunks); Mesh* render(const Chunk* chunk, const ChunksStorage* chunks); + Mesh* createMesh(); VoxelsVolume* getVoxelsBuffer() const; }; diff --git a/src/frontend/graphics/ChunksRenderer.cpp b/src/frontend/graphics/ChunksRenderer.cpp new file mode 100644 index 00000000..fee415fe --- /dev/null +++ b/src/frontend/graphics/ChunksRenderer.cpp @@ -0,0 +1,150 @@ +#include "ChunksRenderer.h" + +#include "../../graphics/Mesh.h" +#include "BlocksRenderer.h" +#include "../../voxels/Chunk.h" +#include "../../world/Level.h" + +#include +#include +#include + +ChunksRenderer::ChunksRenderer(Level* level, const ContentGfxCache* cache, const EngineSettings& settings) +: level(level), cache(cache), settings(settings) { + const int MAX_FULL_CUBES = 3000; + renderer = std::make_unique( + 9 * 6 * 6 * MAX_FULL_CUBES, level->content, cache, settings + ); + + const uint num_threads = std::thread::hardware_concurrency(); + for (uint i = 0; i < num_threads; i++) { + threads.emplace_back(&ChunksRenderer::threadLoop, this, i); + workersBlocked.emplace_back(); + } + std::cout << "created " << num_threads << " chunks rendering threads" << std::endl; +} + +ChunksRenderer::~ChunksRenderer() { + { + std::unique_lock lock(jobsMutex); + working = false; + } + + resultsMutex.lock(); + while (!results.empty()) { + mesh_entry entry = results.front(); + results.pop(); + entry.locked = false; + entry.variable.notify_all(); + } + resultsMutex.unlock(); + + jobsMutexCondition.notify_all(); + for (auto& thread : threads) { + thread.join(); + } +} + +void ChunksRenderer::threadLoop(int index) { + const int MAX_FULL_CUBES = 3000; + BlocksRenderer renderer( + 9 * 6 * 6 * MAX_FULL_CUBES, level->content, cache, settings + ); + + std::condition_variable variable; + std::mutex mutex; + bool locked = false; + while (working) { + std::shared_ptr chunk; + { + std::unique_lock lock(jobsMutex); + jobsMutexCondition.wait(lock, [this] { + return !jobs.empty() || !working; + }); + if (!working) { + break; + } + chunk = jobs.front(); + jobs.pop(); + } + process(chunk, renderer); + { + resultsMutex.lock(); + results.push(mesh_entry {renderer, variable, index, locked, glm::ivec2(chunk->x, chunk->z)}); + locked = true; + resultsMutex.unlock(); + } + { + std::unique_lock lock(mutex); + variable.wait(lock, [&] { + return !working || !locked; + }); + } + } +} + +void ChunksRenderer::process(std::shared_ptr chunk, BlocksRenderer& renderer) { + renderer.build(chunk.get(), level->chunksStorage); +} + +std::shared_ptr ChunksRenderer::render(std::shared_ptr chunk, bool important) { + chunk->setModified(false); + + if (important) { + Mesh* mesh = renderer->render(chunk.get(), level->chunksStorage); + auto sptr = std::shared_ptr(mesh); + meshes[glm::ivec2(chunk->x, chunk->z)] = sptr; + return sptr; + } + + glm::ivec2 key(chunk->x, chunk->z); + if (inwork.find(key) != inwork.end()) { + return nullptr; + } + + inwork[key] = true; + jobsMutex.lock(); + jobs.push(chunk); + jobsMutex.unlock(); + jobsMutexCondition.notify_one(); + return nullptr; +} + +void ChunksRenderer::unload(Chunk* chunk) { + auto found = meshes.find(glm::ivec2(chunk->x, chunk->z)); + if (found != meshes.end()) { + meshes.erase(found); + } +} + +std::shared_ptr ChunksRenderer::getOrRender(std::shared_ptr chunk, bool important) { + auto found = meshes.find(glm::ivec2(chunk->x, chunk->z)); + if (found != meshes.end()){ + if (chunk->isModified()) { + render(chunk, important); + } + return found->second; + } + return render(chunk, important); +} + +std::shared_ptr ChunksRenderer::get(Chunk* chunk) { + auto found = meshes.find(glm::ivec2(chunk->x, chunk->z)); + if (found != meshes.end()) { + return found->second; + } + return nullptr; +} + +void ChunksRenderer::update() { + resultsMutex.lock(); + while (!results.empty()) { + mesh_entry entry = results.front(); + results.pop(); + meshes[entry.key] = std::shared_ptr(entry.renderer.createMesh()); + inwork.erase(entry.key); + entry.locked = false; + entry.variable.notify_all(); + } + resultsMutex.unlock(); +} diff --git a/src/frontend/graphics/ChunksRenderer.h b/src/frontend/graphics/ChunksRenderer.h new file mode 100644 index 00000000..d442d827 --- /dev/null +++ b/src/frontend/graphics/ChunksRenderer.h @@ -0,0 +1,67 @@ +#ifndef SRC_GRAPHICS_CHUNKSRENDERER_H_ +#define SRC_GRAPHICS_CHUNKSRENDERER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../voxels/Block.h" +#include "../../voxels/ChunksStorage.h" +#include "../../settings.h" + +class Mesh; +class Chunk; +class Level; +class BlocksRenderer; +class ContentGfxCache; + +struct mesh_entry { + BlocksRenderer& renderer; + std::condition_variable& variable; + int workerIndex; + bool& locked; + glm::ivec2 key; +}; + +class ChunksRenderer { + std::unique_ptr renderer; + Level* level; + std::unordered_map> meshes; + std::unordered_map inwork; + std::vector threads; + + std::queue results; + std::mutex resultsMutex; + + std::queue> jobs; + std::condition_variable jobsMutexCondition; + std::mutex jobsMutex; + + bool working = true; + const ContentGfxCache* cache; + const EngineSettings& settings; + std::vector> workersBlocked; + + void threadLoop(int index); + void process(std::shared_ptr chunk, BlocksRenderer& renderer); +public: + ChunksRenderer(Level* level, + const ContentGfxCache* cache, + const EngineSettings& settings); + virtual ~ChunksRenderer(); + + std::shared_ptr render(std::shared_ptr chunk, bool important); + void unload(Chunk* chunk); + + std::shared_ptr getOrRender(std::shared_ptr chunk, bool important); + std::shared_ptr get(Chunk* chunk); + + void update(); +}; + +#endif // SRC_GRAPHICS_CHUNKSRENDERER_H_ \ No newline at end of file diff --git a/src/graphics/ChunksRenderer.cpp b/src/graphics/ChunksRenderer.cpp deleted file mode 100644 index 3b39e235..00000000 --- a/src/graphics/ChunksRenderer.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "ChunksRenderer.h" - -#include "Mesh.h" -#include "BlocksRenderer.h" -#include "../voxels/Chunk.h" -#include "../world/Level.h" - -#include -#include - -using glm::ivec2; - -ChunksRenderer::ChunksRenderer(Level* level, const ContentGfxCache* cache, const EngineSettings& settings) : level(level) { - const int MAX_FULL_CUBES = 3000; - renderer = new BlocksRenderer(9 * 6 * 6 * MAX_FULL_CUBES, level->content, cache, settings); -} - -ChunksRenderer::~ChunksRenderer() { - delete renderer; -} - -std::shared_ptr ChunksRenderer::render(Chunk* chunk) { - chunk->setModified(false); - Mesh* mesh = renderer->render(chunk, level->chunksStorage); - auto sptr = std::shared_ptr(mesh); - meshes[ivec2(chunk->x, chunk->z)] = sptr; - return sptr; -} - -void ChunksRenderer::unload(Chunk* chunk) { - auto found = meshes.find(ivec2(chunk->x, chunk->z)); - if (found != meshes.end()) { - meshes.erase(found); - } -} - -std::shared_ptr ChunksRenderer::getOrRender(Chunk* chunk) { - auto found = meshes.find(ivec2(chunk->x, chunk->z)); - if (found != meshes.end() && !chunk->isModified()){ - return found->second; - } - return render(chunk); -} - -std::shared_ptr ChunksRenderer::get(Chunk* chunk) { - auto found = meshes.find(ivec2(chunk->x, chunk->z)); - if (found != meshes.end()) { - return found->second; - } - return nullptr; -} diff --git a/src/graphics/ChunksRenderer.h b/src/graphics/ChunksRenderer.h deleted file mode 100644 index a473872e..00000000 --- a/src/graphics/ChunksRenderer.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef SRC_GRAPHICS_CHUNKSRENDERER_H_ -#define SRC_GRAPHICS_CHUNKSRENDERER_H_ - -#include -#include -#include -#include "../voxels/Block.h" -#include "../voxels/ChunksStorage.h" -#include "../settings.h" - -class Mesh; -class Chunk; -class Level; -class BlocksRenderer; -class ContentGfxCache; - -class ChunksRenderer { - BlocksRenderer* renderer; - Level* level; - std::unordered_map> meshes; -public: - ChunksRenderer(Level* level, - const ContentGfxCache* cache, - const EngineSettings& settings); - virtual ~ChunksRenderer(); - - std::shared_ptr render(Chunk* chunk); - void unload(Chunk* chunk); - - std::shared_ptr getOrRender(Chunk* chunk); - std::shared_ptr get(Chunk* chunk); - -}; - -#endif // SRC_GRAPHICS_CHUNKSRENDERER_H_ \ No newline at end of file diff --git a/src/util/stringutil.cpp b/src/util/stringutil.cpp index 605eb741..b4ca79bd 100644 --- a/src/util/stringutil.cpp +++ b/src/util/stringutil.cpp @@ -291,3 +291,23 @@ double util::parse_double(const std::string& str) { double util::parse_double(const std::string& str, size_t offset, size_t len) { return parse_double(str.substr(offset, len)); } + +std::vector util::split(const std::string& str, char delimiter) { + std::vector result; + std::stringstream ss(str); + std::string tmp; + while (std::getline(ss, tmp, delimiter)) { + result.push_back(tmp); + } + return result; +} + +std::vector util::split(const std::wstring& str, char delimiter) { + std::vector result; + std::wstringstream ss(str); + std::wstring tmp; + while (std::getline(ss, tmp, static_cast(delimiter))) { + result.push_back(tmp); + } + return result; +} diff --git a/src/util/stringutil.h b/src/util/stringutil.h index 8c30cbc6..de43de15 100644 --- a/src/util/stringutil.h +++ b/src/util/stringutil.h @@ -33,6 +33,9 @@ namespace util { extern double parse_double(const std::string& str); extern double parse_double(const std::string& str, size_t offset, size_t len); + + extern std::vector split(const std::string& str, char delimiter); + extern std::vector split(const std::wstring& str, char delimiter); } #endif // UTIL_STRINGUTIL_H_ \ No newline at end of file diff --git a/src/voxels/WorldGenerator.h b/src/voxels/WorldGenerator.h index ef017315..137f5aa5 100644 --- a/src/voxels/WorldGenerator.h +++ b/src/voxels/WorldGenerator.h @@ -21,6 +21,7 @@ protected: blockid_t const idBazalt; public: WorldGenerator(const Content* content); + virtual ~WorldGenerator() = default; virtual void generate(voxel* voxels, int x, int z, int seed) = 0; };