fix translucent rendering order across chunk boundaries

This commit is contained in:
eliotbyte 2025-10-25 18:02:57 +03:00
parent 1bea18294f
commit b201151500

View file

@ -329,7 +329,7 @@ void ChunksRenderer::drawSortedMeshes(const Camera& camera, Shader& shader) {
static int frameid = 0; static int frameid = 0;
frameid++; frameid++;
bool culling = settings.graphics.frustumCulling.get(); const bool culling = settings.graphics.frustumCulling.get();
const auto& chunks = this->chunks.getChunks(); const auto& chunks = this->chunks.getChunks();
const auto& cameraPos = camera.position; const auto& cameraPos = camera.position;
const auto& atlas = assets.require<Atlas>("blocks"); const auto& atlas = assets.require<Atlas>("blocks");
@ -338,60 +338,78 @@ void ChunksRenderer::drawSortedMeshes(const Camera& camera, Shader& shader) {
atlas.getTexture()->bind(); atlas.getTexture()->bind();
shader.uniformMatrix("u_model", glm::mat4(1.0f)); shader.uniformMatrix("u_model", glm::mat4(1.0f));
shader.uniform1i("u_alphaClip", false); shader.uniform1i("u_alphaClip", false);
struct VisibleChunkTrans {
glm::ivec2 key;
const std::shared_ptr<Chunk>* chunkPtr;
long long nearestDist2;
};
std::vector<VisibleChunkTrans> order;
order.reserve(indices.size());
// Build per-chunk nearest distance for translucent entries
for (const auto& index : indices) { for (const auto& index : indices) {
const auto& chunk = chunks[index.index]; const auto& chunk = chunks[index.index];
if (chunk == nullptr || !chunk->flags.lighted) { if (chunk == nullptr || !chunk->flags.lighted) {
continue; continue;
} }
const auto& found = meshes.find(glm::ivec2(chunk->x, chunk->z)); const auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
if (found == meshes.end() || found->second.sortingMeshData.entries.empty()) { if (found == meshes.end()) {
continue;
}
const auto& entries = found->second.sortingMeshData.entries;
if (entries.empty()) {
continue; continue;
} }
if (culling) { if (culling) {
glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D); 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); glm::vec3 max(chunk->x * CHUNK_W + CHUNK_W, chunk->top, chunk->z * CHUNK_D + CHUNK_D);
const auto& found2 = meshes.find(glm::ivec2(chunk->x, chunk->z)); const auto& aabb = found->second.localAabb;
if (found2 != meshes.end()) { if (aabb.size().x > 0.0f || aabb.size().y > 0.0f || aabb.size().z > 0.0f) {
const auto& aabb = found2->second.localAabb; min = glm::vec3(chunk->x * CHUNK_W + aabb.min().x + 0.5f,
if (aabb.size().x > 0.0f || aabb.size().y > 0.0f || aabb.size().z > 0.0f) { (std::max)(static_cast<float>(chunk->bottom), aabb.min().y + 0.5f),
min = glm::vec3(chunk->x * CHUNK_W + aabb.min().x + 0.5f, chunk->z * CHUNK_D + aabb.min().z + 0.5f);
(std::max)(static_cast<float>(chunk->bottom), aabb.min().y + 0.5f), max = glm::vec3(chunk->x * CHUNK_W + aabb.max().x + 0.5f,
chunk->z * CHUNK_D + aabb.min().z + 0.5f); (std::min)(static_cast<float>(chunk->top), aabb.max().y + 0.5f),
max = glm::vec3(chunk->x * CHUNK_W + aabb.max().x + 0.5f, chunk->z * CHUNK_D + aabb.max().z + 0.5f);
(std::min)(static_cast<float>(chunk->top), aabb.max().y + 0.5f),
chunk->z * CHUNK_D + aabb.max().z + 0.5f);
}
} }
if (!frustum.isBoxVisible(min, max)) continue; if (!frustum.isBoxVisible(min, max)) continue;
} }
auto& chunkEntries = found->second.sortingMeshData.entries; long long nearest = LLONG_MAX;
for (const auto& e : entries) {
long long d2 = static_cast<long long>(glm::distance2(e.position, cameraPos));
if (d2 < nearest) nearest = d2;
}
order.push_back(VisibleChunkTrans{glm::ivec2(chunk->x, chunk->z), &chunks[index.index], nearest});
}
if (chunkEntries.size() == 1) { // Sort chunks by nearest translucent distance back-to-front (far to near)
auto& entry = chunkEntries.at(0); std::sort(order.begin(), order.end(), [](const VisibleChunkTrans& a, const VisibleChunkTrans& b) {
if (found->second.sortedMesh == nullptr) { return a.nearestDist2 > b.nearestDist2;
found->second.sortedMesh = std::make_unique<Mesh<ChunkVertex>>( });
entry.vertexData.data(), entry.vertexData.size()
// Draw per-chunk sorted mesh (keeps GPU buffers and avoids per-frame repack)
for (const auto& item : order) {
const auto& chunk = *item.chunkPtr;
const auto found = meshes.find(item.key);
if (found == meshes.end()) continue;
auto& chunkEntries = found->second.sortingMeshData.entries;
if (chunkEntries.empty()) continue;
// Keep per-chunk internal order up-to-date occasionally
if (found->second.sortedMesh == nullptr || (frameid + chunk->x) % sortInterval == 0) {
for (auto& entry : chunkEntries) {
entry.distance = static_cast<long long>(
glm::distance2(entry.position, cameraPos)
); );
} }
found->second.sortedMesh->draw();
continue;
}
for (auto& entry : chunkEntries) {
entry.distance = static_cast<long long>(
glm::distance2(entry.position, cameraPos)
);
}
if (found->second.sortedMesh == nullptr ||
(frameid + chunk->x) % sortInterval == 0) {
std::sort(chunkEntries.begin(), chunkEntries.end()); std::sort(chunkEntries.begin(), chunkEntries.end());
size_t size = 0; size_t size = 0;
for (const auto& entry : chunkEntries) { for (const auto& entry : chunkEntries) {
size += entry.vertexData.size(); size += entry.vertexData.size();
} }
static util::Buffer<ChunkVertex> buffer; static util::Buffer<ChunkVertex> buffer;
if (buffer.size() < size) { if (buffer.size() < size) {
buffer = util::Buffer<ChunkVertex>(size); buffer = util::Buffer<ChunkVertex>(size);