diff --git a/doc/en/block-properties.md b/doc/en/block-properties.md index cef58a9ab..18c068caa 100644 --- a/doc/en/block-properties.md +++ b/doc/en/block-properties.md @@ -39,6 +39,12 @@ Block model type from list: Integer specifying number of block draw group (render order). Used for semi-transparent blocks. +### *translucent* + +Enables translucency support in block textures (examples: water, ice). +Should only be used when needed, as it impacts performance. +Not required for full transparency (grass, flowers). + ### *rotation* Rotation profile (set of available block rotations and behaviour of placing block rotation) from list: diff --git a/doc/ru/block-properties.md b/doc/ru/block-properties.md index 62ef8ec43..79b08da25 100644 --- a/doc/ru/block-properties.md +++ b/doc/ru/block-properties.md @@ -40,6 +40,12 @@ Целое число определяющее номер группы отрисовки данного блока. Актуально для полупрозрачных блоков - решает проблемы невидимых сторон блоков за этим блоком. +### Полупрозрачность - *translucent* + +Включает поддержку полупрозрачности в текстурах блока (примеры: вода, лёд). +Следует использовать только при надобности, так как влияет на производительность. +Не требуется для полной прозрачности (трава, цветы). + ### Вращение - *rotation* Профиль вращения (набор положений, в которые можно установить блок) из списка: diff --git a/res/content/base/blocks/glass.json b/res/content/base/blocks/glass.json index 902a2cf15..9bd4f0214 100644 --- a/res/content/base/blocks/glass.json +++ b/res/content/base/blocks/glass.json @@ -3,5 +3,6 @@ "material": "base:glass", "draw-group": 2, "light-passing": true, - "sky-light-passing": true + "sky-light-passing": true, + "translucent": true } diff --git a/res/content/base/blocks/ice.json b/res/content/base/blocks/ice.json new file mode 100644 index 000000000..19d9af12e --- /dev/null +++ b/res/content/base/blocks/ice.json @@ -0,0 +1,7 @@ +{ + "texture": "ice", + "material": "base:glass", + "draw-group": 4, + "light-passing": true, + "translucent": true +} diff --git a/res/content/base/blocks/water.json b/res/content/base/blocks/water.json index 943640e84..f77850447 100644 --- a/res/content/base/blocks/water.json +++ b/res/content/base/blocks/water.json @@ -6,5 +6,6 @@ "sky-light-passing": false, "obstacle": false, "selectable": false, - "replaceable": true + "replaceable": true, + "translucent": true } diff --git a/res/content/base/content.json b/res/content/base/content.json index cec825a85..db13bae2b 100644 --- a/res/content/base/content.json +++ b/res/content/base/content.json @@ -1,6 +1,8 @@ { - "items": [ - "bazalt_breaker" + "entities": [ + "drop", + "player", + "falling_block" ], "blocks": [ "dirt", @@ -27,11 +29,10 @@ "lightbulb", "torch", "wooden_door", - "coal_ore" + "coal_ore", + "ice" ], - "entities": [ - "drop", - "player", - "falling_block" + "items": [ + "bazalt_breaker" ] } \ No newline at end of file diff --git a/res/content/base/textures/blocks/ice.png b/res/content/base/textures/blocks/ice.png new file mode 100644 index 000000000..707ee89ae Binary files /dev/null and b/res/content/base/textures/blocks/ice.png differ diff --git a/res/shaders/main.glslf b/res/shaders/main.glslf index 4c1cb0141..7db1a544d 100644 --- a/res/shaders/main.glslf +++ b/res/shaders/main.glslf @@ -9,14 +9,14 @@ uniform samplerCube u_cubemap; uniform vec3 u_fogColor; uniform float u_fogFactor; uniform float u_fogCurve; +uniform bool u_alphaClip; void main() { vec3 fogColor = texture(u_cubemap, a_dir).rgb; vec4 tex_color = texture(u_texture0, a_texCoord); float depth = (a_distance/256.0); float alpha = a_color.a * tex_color.a; - // anyway it's any alpha-test alternative required - if (alpha < 0.3f) + if (u_alphaClip && alpha < 0.9f) discard; f_color = mix(a_color * tex_color, vec4(fogColor,1.0), min(1.0, pow(depth*u_fogFactor, u_fogCurve))); diff --git a/src/constants.hpp b/src/constants.hpp index 12e12c758..84d8b22c1 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -35,9 +35,6 @@ inline constexpr int CHUNK_D = 16; inline constexpr uint VOXEL_USER_BITS = 8; inline constexpr uint VOXEL_USER_BITS_OFFSET = sizeof(blockstate_t)*8-VOXEL_USER_BITS; -/// @brief pixel size of an item inventory icon -inline constexpr int ITEM_ICON_SIZE = 48; - /// @brief chunk volume (count of voxels per Chunk) inline constexpr int CHUNK_VOL = (CHUNK_W * CHUNK_H * CHUNK_D); @@ -53,6 +50,11 @@ inline constexpr uint vox_index(uint x, uint y, uint z, uint w=CHUNK_W, uint d=C return (y * d + z) * w + x; } +/// @brief pixel size of an item inventory icon +inline constexpr int ITEM_ICON_SIZE = 48; + +inline constexpr int TRANSLUCENT_BLOCKS_SORT_INTERVAL = 8; + inline const std::string SHADERS_FOLDER = "shaders"; inline const std::string TEXTURES_FOLDER = "textures"; inline const std::string FONTS_FOLDER = "fonts"; diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index a801500e3..4414999dd 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -333,6 +333,7 @@ void ContentLoader::loadBlock( root.at("inventory-size").get(def.inventorySize); root.at("tick-interval").get(def.tickInterval); root.at("overlay-texture").get(def.overlayTexture); + root.at("translucent").get(def.translucent); if (root.has("fields")) { def.dataStruct = std::make_unique(); diff --git a/src/graphics/core/Batch2D.cpp b/src/graphics/core/Batch2D.cpp index 13b485862..c4c67db29 100644 --- a/src/graphics/core/Batch2D.cpp +++ b/src/graphics/core/Batch2D.cpp @@ -11,7 +11,7 @@ inline constexpr uint B2D_VERTEX_SIZE = 8; Batch2D::Batch2D(size_t capacity) : capacity(capacity), color(1.0f){ - const vattr attrs[] = { + const VertexAttribute attrs[] = { {2}, {2}, {4}, {0} }; diff --git a/src/graphics/core/Batch3D.cpp b/src/graphics/core/Batch3D.cpp index 1770fc21d..a81f4ffb8 100644 --- a/src/graphics/core/Batch3D.cpp +++ b/src/graphics/core/Batch3D.cpp @@ -12,7 +12,7 @@ inline constexpr uint B3D_VERTEX_SIZE = 9; Batch3D::Batch3D(size_t capacity) : capacity(capacity) { - const vattr attrs[] = { + const VertexAttribute attrs[] = { {3}, {2}, {4}, {0} }; diff --git a/src/graphics/core/LineBatch.cpp b/src/graphics/core/LineBatch.cpp index dd3f8067b..aed8a2496 100644 --- a/src/graphics/core/LineBatch.cpp +++ b/src/graphics/core/LineBatch.cpp @@ -6,7 +6,7 @@ inline constexpr uint LB_VERTEX_SIZE = (3+4); LineBatch::LineBatch(size_t capacity) : capacity(capacity) { - const vattr attrs[] = { {3},{4}, {0} }; + const VertexAttribute attrs[] = { {3},{4}, {0} }; buffer = std::make_unique(capacity * LB_VERTEX_SIZE * 2); mesh = std::make_unique(buffer.get(), 0, attrs); index = 0; diff --git a/src/graphics/core/Mesh.cpp b/src/graphics/core/Mesh.cpp index 78380ae1b..e8cffe6ff 100644 --- a/src/graphics/core/Mesh.cpp +++ b/src/graphics/core/Mesh.cpp @@ -4,7 +4,7 @@ int Mesh::meshesCount = 0; int Mesh::drawCalls = 0; -inline size_t calc_vertex_size(const vattr* attrs) { +inline size_t calc_vertex_size(const VertexAttribute* attrs) { size_t vertexSize = 0; for (int i = 0; attrs[i].size; i++) { vertexSize += attrs[i].size; @@ -19,10 +19,10 @@ Mesh::Mesh(const MeshData& data) data.indices.size(), data.attrs.data()) {} -Mesh::Mesh(const float* vertexBuffer, size_t vertices, const int* indexBuffer, size_t indices, const vattr* attrs) : +Mesh::Mesh(const float* vertexBuffer, size_t vertices, const int* indexBuffer, size_t indices, const VertexAttribute* attrs) : ibo(0), - vertices(vertices), - indices(indices) + vertices(0), + indices(0) { meshesCount++; vertexSize = 0; @@ -58,10 +58,9 @@ void Mesh::reload(const float* vertexBuffer, size_t vertices, const int* indexBu glBindVertexArray(vao); glBindBuffer(GL_ARRAY_BUFFER, vbo); if (vertexBuffer != nullptr && vertices != 0) { - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertexSize * vertices, vertexBuffer, GL_STATIC_DRAW); - } - else { - glBufferData(GL_ARRAY_BUFFER, 0, {}, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertexSize * vertices, vertexBuffer, GL_STREAM_DRAW); + } else { + glBufferData(GL_ARRAY_BUFFER, 0, {}, GL_STREAM_DRAW); } if (indexBuffer != nullptr && indices != 0) { if (ibo == 0) glGenBuffers(1, &ibo); @@ -75,7 +74,7 @@ void Mesh::reload(const float* vertexBuffer, size_t vertices, const int* indexBu this->indices = indices; } -void Mesh::draw(unsigned int primitive){ +void Mesh::draw(unsigned int primitive) const { drawCalls++; glBindVertexArray(vao); if (ibo != 0) { @@ -87,6 +86,6 @@ void Mesh::draw(unsigned int primitive){ glBindVertexArray(0); } -void Mesh::draw() { +void Mesh::draw() const { draw(GL_TRIANGLES); } diff --git a/src/graphics/core/Mesh.hpp b/src/graphics/core/Mesh.hpp index 87c8e69b4..482c82bfc 100644 --- a/src/graphics/core/Mesh.hpp +++ b/src/graphics/core/Mesh.hpp @@ -14,8 +14,8 @@ class Mesh { size_t vertexSize; public: Mesh(const MeshData& data); - Mesh(const float* vertexBuffer, size_t vertices, const int* indexBuffer, size_t indices, const vattr* attrs); - Mesh(const float* vertexBuffer, size_t vertices, const vattr* attrs) : + Mesh(const float* vertexBuffer, size_t vertices, const int* indexBuffer, size_t indices, const VertexAttribute* attrs); + Mesh(const float* vertexBuffer, size_t vertices, const VertexAttribute* attrs) : Mesh(vertexBuffer, vertices, nullptr, 0, attrs) {}; ~Mesh(); @@ -28,10 +28,10 @@ class Mesh { /// @brief Draw mesh with specified primitives type /// @param primitive primitives type - void draw(unsigned int primitive); + void draw(unsigned int primitive) const; /// @brief Draw mesh as triangles - void draw(); + void draw() const; /// @brief Total numbers of alive mesh objects static int meshesCount; diff --git a/src/graphics/core/MeshData.hpp b/src/graphics/core/MeshData.hpp index 62b329037..392201197 100644 --- a/src/graphics/core/MeshData.hpp +++ b/src/graphics/core/MeshData.hpp @@ -6,7 +6,7 @@ #include "util/Buffer.hpp" /// @brief Vertex attribute info -struct vattr { +struct VertexAttribute { ubyte size; }; @@ -14,7 +14,7 @@ struct vattr { struct MeshData { util::Buffer vertices; util::Buffer indices; - util::Buffer attrs; + util::Buffer attrs; MeshData() = default; @@ -24,7 +24,7 @@ struct MeshData { MeshData( util::Buffer vertices, util::Buffer indices, - util::Buffer attrs + util::Buffer attrs ) : vertices(std::move(vertices)), indices(std::move(indices)), attrs(std::move(attrs)) {} diff --git a/src/graphics/core/PostProcessing.cpp b/src/graphics/core/PostProcessing.cpp index 7352c3b55..3478ca08f 100644 --- a/src/graphics/core/PostProcessing.cpp +++ b/src/graphics/core/PostProcessing.cpp @@ -14,7 +14,7 @@ PostProcessing::PostProcessing() { -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f }; - vattr attrs[] {{2}, {0}}; + VertexAttribute attrs[] {{2}, {0}}; quadMesh = std::make_unique(vertices, 6, attrs); } diff --git a/src/graphics/render/BlocksRenderer.cpp b/src/graphics/render/BlocksRenderer.cpp index bf55ef126..cb590bcb4 100644 --- a/src/graphics/render/BlocksRenderer.cpp +++ b/src/graphics/render/BlocksRenderer.cpp @@ -12,7 +12,6 @@ #include -const uint BlocksRenderer::VERTEX_SIZE = 6; const glm::vec3 BlocksRenderer::SUN_VECTOR (0.411934f, 0.863868f, -0.279161f); BlocksRenderer::BlocksRenderer( @@ -21,7 +20,7 @@ BlocksRenderer::BlocksRenderer( const ContentGfxCache& cache, const EngineSettings& settings ) : content(content), - vertexBuffer(std::make_unique(capacity * VERTEX_SIZE)), + vertexBuffer(std::make_unique(capacity * CHUNK_VERTEX_SIZE)), indexBuffer(std::make_unique(capacity)), vertexOffset(0), indexOffset(0), @@ -85,7 +84,7 @@ void BlocksRenderer::face( const glm::vec4(&lights)[4], const glm::vec4& tint ) { - if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) { + if (vertexOffset + CHUNK_VERTEX_SIZE * 4 > capacity) { overflow = true; return; } @@ -125,7 +124,7 @@ void BlocksRenderer::faceAO( const UVRegion& region, bool lights ) { - if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) { + if (vertexOffset + CHUNK_VERTEX_SIZE * 4 > capacity) { overflow = true; return; } @@ -163,7 +162,7 @@ void BlocksRenderer::face( glm::vec4 tint, bool lights ) { - if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) { + if (vertexOffset + CHUNK_VERTEX_SIZE * 4 > capacity) { overflow = true; return; } @@ -288,7 +287,7 @@ void BlocksRenderer::blockCustomModel( const auto& model = cache.getModel(block->rt.id); for (const auto& mesh : model.meshes) { - if (vertexOffset + BlocksRenderer::VERTEX_SIZE * mesh.vertices.size() > capacity) { + if (vertexOffset + CHUNK_VERTEX_SIZE * mesh.vertices.size() > capacity) { overflow = true; return; } @@ -433,21 +432,9 @@ glm::vec4 BlocksRenderer::pickSoftLight( right, up); } -void BlocksRenderer::render(const voxel* voxels) { - int totalBegin = chunk->bottom * (CHUNK_W * CHUNK_D); - int totalEnd = chunk->top * (CHUNK_W * CHUNK_D); - - int beginEnds[256][2] {}; - for (int i = totalBegin; i < totalEnd; i++) { - const voxel& vox = voxels[i]; - blockid_t id = vox.id; - const auto& def = *blockDefsCache[id]; - - if (beginEnds[def.drawGroup][0] == 0) { - beginEnds[def.drawGroup][0] = i+1; - } - beginEnds[def.drawGroup][1] = i; - } +void BlocksRenderer::render( + const voxel* voxels, int beginEnds[256][2] +) { for (const auto drawGroup : *content.drawGroups) { int begin = beginEnds[drawGroup][0]; if (begin == 0) { @@ -462,13 +449,13 @@ void BlocksRenderer::render(const voxel* voxels) { if (id == 0 || def.drawGroup != drawGroup || state.segment) { continue; } + if (def.translucent) { + 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) + 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); @@ -503,43 +490,185 @@ void BlocksRenderer::render(const voxel* voxels) { } } +SortingMeshData BlocksRenderer::renderTranslucent( + const voxel* voxels, int beginEnds[256][2] +) { + SortingMeshData sortingMesh {{}}; + + AABB aabb {}; + bool aabbInit = false; + size_t totalSize = 0; + for (const auto drawGroup : *content.drawGroups) { + int begin = beginEnds[drawGroup][0]; + if (begin == 0) { + continue; + } + int end = beginEnds[drawGroup][1]; + for (int i = begin-1; i <= end; i++) { + const voxel& vox = voxels[i]; + blockid_t id = vox.id; + blockstate state = vox.state; + const auto& def = *blockDefsCache[id]; + if (id == 0 || def.drawGroup != drawGroup || state.segment) { + continue; + } + if (!def.translucent) { + 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.state, !def.shadeless, + def.ambientOcclusion); + break; + case BlockModel::xsprite: { + blockXSprite(x, y, z, glm::vec3(1.0f), + texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f); + break; + } + case BlockModel::aabb: { + blockAABB({x, y, z}, texfaces, &def, vox.state.rotation, + !def.shadeless, def.ambientOcclusion); + break; + } + case BlockModel::custom: { + blockCustomModel({x, y, z}, &def, vox.state.rotation, + !def.shadeless, def.ambientOcclusion); + break; + } + default: + break; + } + if (vertexOffset == 0) { + continue; + } + SortingMeshEntry entry { + glm::vec3( + x + chunk->x * CHUNK_W + 0.5f, + y + 0.5f, + z + chunk->z * CHUNK_D + 0.5f + ), + util::Buffer(indexSize * CHUNK_VERTEX_SIZE)}; + + totalSize += entry.vertexData.size(); + + for (int j = 0; j < indexSize; j++) { + std::memcpy( + entry.vertexData.data() + j * CHUNK_VERTEX_SIZE, + vertexBuffer.get() + indexBuffer[j] * CHUNK_VERTEX_SIZE, + sizeof(float) * CHUNK_VERTEX_SIZE + ); + float& vx = entry.vertexData[j * CHUNK_VERTEX_SIZE + 0]; + float& vy = entry.vertexData[j * CHUNK_VERTEX_SIZE + 1]; + float& vz = entry.vertexData[j * CHUNK_VERTEX_SIZE + 2]; + + if (!aabbInit) { + aabbInit = true; + aabb.a = aabb.b = {vx, vy, vz}; + } else { + aabb.addPoint(glm::vec3(vx, vy, vz)); + } + vx += chunk->x * CHUNK_W + 0.5f; + vy += 0.5f; + vz += chunk->z * CHUNK_D + 0.5f; + } + sortingMesh.entries.push_back(std::move(entry)); + vertexOffset = 0; + indexOffset = indexSize = 0; + } + } + + // additional powerful optimization + auto size = aabb.size(); + if ((size.y < 0.01f || size.x < 0.01f || size.z < 0.01f) && + sortingMesh.entries.size() > 1) { + SortingMeshEntry newEntry { + sortingMesh.entries[0].position, + util::Buffer(totalSize) + }; + size_t offset = 0; + for (const auto& entry : sortingMesh.entries) { + std::memcpy( + newEntry.vertexData.data() + offset, + entry.vertexData.data(), entry.vertexData.size() * sizeof(float) + ); + offset += entry.vertexData.size(); + } + return SortingMeshData {{std::move(newEntry)}}; + } + return sortingMesh; +} + void BlocksRenderer::build(const Chunk* chunk, const Chunks* chunks) { this->chunk = chunk; voxelsBuffer->setPosition( chunk->x * CHUNK_W - voxelBufferPadding, 0, chunk->z * CHUNK_D - voxelBufferPadding); chunks->getVoxels(voxelsBuffer.get(), settings.graphics.backlight.get()); - overflow = false; - vertexOffset = 0; - indexOffset = indexSize = 0; + if (voxelsBuffer->pickBlockId( chunk->x * CHUNK_W, 0, chunk->z * CHUNK_D ) == BLOCK_VOID) { cancelled = true; return; } - cancelled = false; const voxel* voxels = chunk->voxels; - render(voxels); + + int totalBegin = chunk->bottom * (CHUNK_W * CHUNK_D); + int totalEnd = chunk->top * (CHUNK_W * CHUNK_D); + + int beginEnds[256][2] {}; + for (int i = totalBegin; i < totalEnd; i++) { + const voxel& vox = voxels[i]; + blockid_t id = vox.id; + const auto& def = *blockDefsCache[id]; + + if (beginEnds[def.drawGroup][0] == 0) { + beginEnds[def.drawGroup][0] = i+1; + } + beginEnds[def.drawGroup][1] = i; + } + cancelled = false; + + overflow = false; + vertexOffset = 0; + indexOffset = indexSize = 0; + + sortingMesh = std::move(renderTranslucent(voxels, beginEnds)); + + overflow = false; + vertexOffset = 0; + indexOffset = indexSize = 0; + + render(voxels, beginEnds); } -MeshData BlocksRenderer::createMesh() { - const vattr attrs[]{ {3}, {2}, {1}, {0} }; - return MeshData( - util::Buffer(vertexBuffer.get(), vertexOffset), - util::Buffer(indexBuffer.get(), indexSize), - util::Buffer({{3}, {2}, {1}, {0}}) - ); +ChunkMeshData BlocksRenderer::createMesh() { + return ChunkMeshData { + MeshData( + util::Buffer(vertexBuffer.get(), vertexOffset), + util::Buffer(indexBuffer.get(), indexSize), + util::Buffer( + CHUNK_VATTRS, sizeof(CHUNK_VATTRS) / sizeof(VertexAttribute) + ) + ), + std::move(sortingMesh)}; } -std::shared_ptr BlocksRenderer::render(const Chunk* chunk, const Chunks* chunks) { +ChunkMesh BlocksRenderer::render(const Chunk* chunk, const Chunks* chunks) { build(chunk, chunks); - const vattr attrs[]{ {3}, {2}, {1}, {0} }; - size_t vcount = vertexOffset / BlocksRenderer::VERTEX_SIZE; - return std::make_shared( - vertexBuffer.get(), vcount, indexBuffer.get(), indexSize, attrs - ); + size_t vcount = vertexOffset / CHUNK_VERTEX_SIZE; + return ChunkMesh{std::make_unique( + vertexBuffer.get(), vcount, indexBuffer.get(), indexSize, CHUNK_VATTRS + ), std::move(sortingMesh)}; } VoxelsVolume* BlocksRenderer::getVoxelsBuffer() const { diff --git a/src/graphics/render/BlocksRenderer.hpp b/src/graphics/render/BlocksRenderer.hpp index 828ddf7d2..c0e0086e0 100644 --- a/src/graphics/render/BlocksRenderer.hpp +++ b/src/graphics/render/BlocksRenderer.hpp @@ -12,6 +12,7 @@ #include "voxels/VoxelsVolume.hpp" #include "graphics/core/MeshData.hpp" #include "maths/util.hpp" +#include "commons.hpp" class Content; class Mesh; @@ -26,7 +27,6 @@ struct UVRegion; class BlocksRenderer { static const glm::vec3 SUN_VECTOR; - static const uint VERTEX_SIZE; const Content& content; std::unique_ptr vertexBuffer; std::unique_ptr indexBuffer; @@ -45,6 +45,8 @@ class BlocksRenderer { util::PseudoRandom randomizer; + SortingMeshData sortingMesh; + void vertex(const glm::vec3& coord, float u, float v, const glm::vec4& light); void index(int a, int b, int c, int d, int e, int f); @@ -115,7 +117,6 @@ class BlocksRenderer { bool isOpenForLight(int x, int y, int z) const; - // Does block allow to see other blocks sides (is it transparent) inline bool isOpen(const glm::ivec3& pos, ubyte group) const { auto id = voxelsBuffer->pickBlockId( @@ -135,7 +136,9 @@ class BlocksRenderer { glm::vec4 pickLight(const glm::ivec3& coord) const; glm::vec4 pickSoftLight(const glm::ivec3& coord, const glm::ivec3& right, const glm::ivec3& up) const; glm::vec4 pickSoftLight(float x, float y, float z, const glm::ivec3& right, const glm::ivec3& up) const; - void render(const voxel* voxels); + + void render(const voxel* voxels, int beginEnds[256][2]); + SortingMeshData renderTranslucent(const voxel* voxels, int beginEnds[256][2]); public: BlocksRenderer( size_t capacity, @@ -146,8 +149,8 @@ class BlocksRenderer { virtual ~BlocksRenderer(); void build(const Chunk* chunk, const Chunks* chunks); - std::shared_ptr render(const Chunk* chunk, const Chunks* chunks); - MeshData createMesh(); + ChunkMesh render(const Chunk* chunk, const Chunks* chunks); + ChunkMeshData createMesh(); VoxelsVolume* getVoxelsBuffer() const; bool isCancelled() const { diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index d0ae56b4d..d3686a860 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -14,10 +14,6 @@ #include "util/listutil.hpp" #include "settings.hpp" -#include -#include -#include - static debug::Logger logger("chunks-render"); size_t ChunksRenderer::visibleChunks = 0; @@ -62,7 +58,11 @@ ChunksRenderer::ChunksRenderer( [&](){return std::make_shared(*level, cache, settings);}, [&](RendererResult& result){ if (!result.cancelled) { - meshes[result.key] = std::make_shared(result.meshData); + auto meshData = std::move(result.meshData); + meshes[result.key] = ChunkMesh { + std::make_unique(meshData.mesh), + std::move(meshData.sortingMesh) + }; } inwork.erase(result.key); }, settings.graphics.chunkMaxRenderers.get()) @@ -78,12 +78,16 @@ ChunksRenderer::ChunksRenderer( ChunksRenderer::~ChunksRenderer() { } -std::shared_ptr ChunksRenderer::render(const std::shared_ptr& chunk, bool important) { +const Mesh* ChunksRenderer::render( + const std::shared_ptr& chunk, bool important +) { chunk->flags.modified = false; if (important) { auto mesh = renderer->render(chunk.get(), level.chunks.get()); - meshes[glm::ivec2(chunk->x, chunk->z)] = mesh; - return mesh; + meshes[glm::ivec2(chunk->x, chunk->z)] = ChunkMesh { + std::move(mesh.mesh), std::move(mesh.sortingMeshData) + }; + return meshes[glm::ivec2(chunk->x, chunk->z)].mesh.get(); } glm::ivec2 key(chunk->x, chunk->z); if (inwork.find(key) != inwork.end()) { @@ -107,7 +111,9 @@ void ChunksRenderer::clear() { threadPool.clearQueue(); } -std::shared_ptr ChunksRenderer::getOrRender(const std::shared_ptr& chunk, bool important) { +const Mesh* ChunksRenderer::getOrRender( + const std::shared_ptr& chunk, bool important +) { auto found = meshes.find(glm::ivec2(chunk->x, chunk->z)); if (found == meshes.end()) { return render(chunk, important); @@ -115,19 +121,19 @@ std::shared_ptr ChunksRenderer::getOrRender(const std::shared_ptr& if (chunk->flags.modified) { render(chunk, important); } - return found->second; + return found->second.mesh.get(); } void ChunksRenderer::update() { threadPool.update(); } -bool ChunksRenderer::drawChunk( +const Mesh* ChunksRenderer::retrieveChunk( size_t index, const Camera& camera, Shader& shader, bool culling ) { auto chunk = level.chunks->getChunks()[index]; if (chunk == nullptr || !chunk->flags.lighted) { - return false; + return nullptr; } float distance = glm::distance( camera.position, @@ -139,7 +145,7 @@ bool ChunksRenderer::drawChunk( ); auto mesh = getOrRender(chunk, distance < CHUNK_W * 1.5f); if (mesh == nullptr) { - return false; + return nullptr; } if (culling) { glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D); @@ -149,13 +155,9 @@ bool ChunksRenderer::drawChunk( chunk->z * CHUNK_D + CHUNK_D ); - if (!frustum.isBoxVisible(min, max)) return false; + if (!frustum.isBoxVisible(min, max)) return nullptr; } - glm::vec3 coord(chunk->x * CHUNK_W + 0.5f, 0.5f, chunk->z * CHUNK_D + 0.5f); - glm::mat4 model = glm::translate(glm::mat4(1.0f), coord); - shader.uniformMatrix("u_model", model); - mesh->draw(); - return true; + return mesh; } void ChunksRenderer::drawChunks( @@ -191,11 +193,108 @@ void ChunksRenderer::drawChunks( bool culling = settings.graphics.frustumCulling.get(); visibleChunks = 0; - //if (GLEW_ARB_multi_draw_indirect && false) { - // TODO: implement Multi Draw Indirect chunks draw - //} else { - for (size_t i = 0; i < indices.size(); i++) { - visibleChunks += drawChunk(indices[i].index, camera, shader, culling); + shader.uniform1i("u_alphaClip", true); + + // TODO: minimize draw calls number + for (size_t i = 0; i < indices.size(); i++) { + auto chunk = chunks.getChunks()[indices[i].index]; + auto mesh = retrieveChunk(indices[i].index, camera, shader, culling); + + if (mesh) { + glm::vec3 coord( + chunk->x * CHUNK_W + 0.5f, 0.5f, chunk->z * CHUNK_D + 0.5f + ); + glm::mat4 model = glm::translate(glm::mat4(1.0f), coord); + shader.uniformMatrix("u_model", model); + mesh->draw(); + visibleChunks++; + } + } +} + +static inline void write_sorting_mesh_entries( + float* buffer, const std::vector& chunkEntries +) { + for (const auto& entry : chunkEntries) { + const auto& vertexData = entry.vertexData; + std::memcpy( + buffer, + vertexData.data(), + vertexData.size() * sizeof(float) + ); + buffer += vertexData.size(); + } +} + +void ChunksRenderer::drawSortedMeshes(const Camera& camera, Shader& shader) { + const int sortInterval = TRANSLUCENT_BLOCKS_SORT_INTERVAL; + static int frameid = 0; + frameid++; + + bool culling = settings.graphics.frustumCulling.get(); + const auto& chunks = level.chunks->getChunks(); + const auto& cameraPos = camera.position; + const auto& atlas = assets.require("blocks"); + + atlas.getTexture()->bind(); + shader.uniformMatrix("u_model", glm::mat4(1.0f)); + shader.uniform1i("u_alphaClip", false); + + for (const auto& index : indices) { + const auto& chunk = chunks[index.index]; + if (chunk == nullptr || !chunk->flags.lighted) { + continue; + } + const auto& found = meshes.find(glm::ivec2(chunk->x, chunk->z)); + if (found == meshes.end() || found->second.sortingMeshData.entries.empty()) { + continue; + } + + glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D); + glm::vec3 max( + chunk->x * CHUNK_W + CHUNK_W, + chunk->top, + chunk->z * CHUNK_D + CHUNK_D + ); + + if (!frustum.isBoxVisible(min, max)) continue; + + auto& chunkEntries = found->second.sortingMeshData.entries; + + if (chunkEntries.size() == 1) { + auto& entry = chunkEntries.at(0); + if (found->second.sortedMesh == nullptr) { + found->second.sortedMesh = std::make_unique( + entry.vertexData.data(), + entry.vertexData.size() / CHUNK_VERTEX_SIZE, + CHUNK_VATTRS + ); + } + found->second.sortedMesh->draw(); + continue; + } + for (auto& entry : chunkEntries) { + entry.distance = static_cast( + glm::distance2(entry.position, cameraPos) + ); } - //} + if (found->second.sortedMesh == nullptr || + (frameid + chunk->x) % sortInterval == 0) { + std::sort(chunkEntries.begin(), chunkEntries.end()); + size_t size = 0; + for (const auto& entry : chunkEntries) { + size += entry.vertexData.size(); + } + + static util::Buffer buffer; + if (buffer.size() < size) { + buffer = util::Buffer(size); + } + write_sorting_mesh_entries(buffer.data(), chunkEntries); + found->second.sortedMesh = std::make_unique( + buffer.data(), size / CHUNK_VERTEX_SIZE, CHUNK_VATTRS + ); + } + found->second.sortedMesh->draw(); + } } diff --git a/src/graphics/render/ChunksRenderer.hpp b/src/graphics/render/ChunksRenderer.hpp index c08266bb7..859bca9eb 100644 --- a/src/graphics/render/ChunksRenderer.hpp +++ b/src/graphics/render/ChunksRenderer.hpp @@ -4,19 +4,21 @@ #include #include #include + #include +#define GLM_ENABLE_EXPERIMENTAL +#include #include "voxels/Block.hpp" -#include "voxels/ChunksStorage.hpp" #include "util/ThreadPool.hpp" #include "graphics/core/MeshData.hpp" +#include "commons.hpp" class Mesh; class Chunk; class Level; class Camera; class Shader; -class Chunks; class Assets; class Frustum; class BlocksRenderer; @@ -35,7 +37,7 @@ struct ChunksSortEntry { struct RendererResult { glm::ivec2 key; bool cancelled; - MeshData meshData; + ChunkMeshData meshData; }; class ChunksRenderer { @@ -45,12 +47,11 @@ class ChunksRenderer { const EngineSettings& settings; std::unique_ptr renderer; - std::unordered_map> meshes; + std::unordered_map meshes; std::unordered_map inwork; std::vector indices; util::ThreadPool, RendererResult> threadPool; - - bool drawChunk( + const Mesh* retrieveChunk( size_t index, const Camera& camera, Shader& shader, bool culling ); public: @@ -63,17 +64,19 @@ class ChunksRenderer { ); virtual ~ChunksRenderer(); - std::shared_ptr render( + const Mesh* render( const std::shared_ptr& chunk, bool important ); void unload(const Chunk* chunk); void clear(); - std::shared_ptr getOrRender( + const Mesh* getOrRender( const std::shared_ptr& chunk, bool important ); void drawChunks(const Camera& camera, Shader& shader); + void drawSortedMeshes(const Camera& camera, Shader& shader); + void update(); static size_t visibleChunks; diff --git a/src/graphics/render/MainBatch.cpp b/src/graphics/render/MainBatch.cpp index 1cbb6c420..e3a87db70 100644 --- a/src/graphics/render/MainBatch.cpp +++ b/src/graphics/render/MainBatch.cpp @@ -6,7 +6,7 @@ #include "voxels/Chunks.hpp" #include "voxels/Chunk.hpp" -static const vattr attrs[] = { +static const VertexAttribute attrs[] = { {3}, {2}, {3}, {1}, {0} }; diff --git a/src/graphics/render/Skybox.cpp b/src/graphics/render/Skybox.cpp index e96e894ac..af3a15870 100644 --- a/src/graphics/render/Skybox.cpp +++ b/src/graphics/render/Skybox.cpp @@ -39,7 +39,7 @@ Skybox::Skybox(uint size, Shader& shader) -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f }; - vattr attrs[] {{2}, {0}}; + VertexAttribute attrs[] {{2}, {0}}; mesh = std::make_unique(vertices, 6, attrs); sprites.push_back(skysprite { diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 1ad16a628..0593305de 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -164,10 +164,18 @@ void WorldRenderer::renderLevel( particles->render(camera, delta * !pause); auto& shader = assets.require("main"); + auto& linesShader = assets.require("lines"); + setupWorldShader(shader, camera, settings, fogFactor); chunks->drawChunks(camera, shader); + if (hudVisible) { + renderLines(camera, linesShader, ctx); + } + shader.use(); + chunks->drawSortedMeshes(camera, shader); + if (!pause) { scripting::on_frontend_render(); } @@ -326,7 +334,6 @@ void WorldRenderer::draw( ctx, camera, *lineBatch, linesShader, showChunkBorders ); } - renderLines(camera, linesShader, ctx); if (player->currentCamera == player->fpCamera) { renderHands(camera, delta * !pause); } diff --git a/src/graphics/render/commons.hpp b/src/graphics/render/commons.hpp new file mode 100644 index 000000000..79b773932 --- /dev/null +++ b/src/graphics/render/commons.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include + +#include "graphics/core/MeshData.hpp" +#include "util/Buffer.hpp" + +/// @brief Chunk mesh vertex attributes +inline const VertexAttribute CHUNK_VATTRS[]{ {3}, {2}, {1}, {0} }; +/// @brief Chunk mesh vertex size divided by sizeof(float) +inline constexpr int CHUNK_VERTEX_SIZE = 6; + +class Mesh; + +struct SortingMeshEntry { + glm::vec3 position; + util::Buffer vertexData; + long long distance; + + inline bool operator<(const SortingMeshEntry& o) const noexcept { + return distance > o.distance; + } +}; + +struct SortingMeshData { + std::vector entries; +}; + +struct ChunkMeshData { + MeshData mesh; + SortingMeshData sortingMesh; +}; + +struct ChunkMesh { + std::unique_ptr mesh; + SortingMeshData sortingMeshData; + std::unique_ptr sortedMesh = nullptr; +}; diff --git a/src/voxels/Block.cpp b/src/voxels/Block.cpp index 51d9662b5..6d4ff7613 100644 --- a/src/voxels/Block.cpp +++ b/src/voxels/Block.cpp @@ -140,6 +140,7 @@ void Block::cloneTo(Block& dst) { dst.inventorySize = inventorySize; dst.tickInterval = tickInterval; dst.overlayTexture = overlayTexture; + dst.translucent = translucent; if (particles) { dst.particles = std::make_unique(*particles); } diff --git a/src/voxels/Block.hpp b/src/voxels/Block.hpp index 26b384691..84835534e 100644 --- a/src/voxels/Block.hpp +++ b/src/voxels/Block.hpp @@ -172,6 +172,9 @@ class Block { /// @brief Turns off block item generation bool hidden = false; + /// @brief Block has semi-transparent texture + bool translucent = false; + /// @brief Set of block physical hitboxes std::vector hitboxes;