mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-12 22:41:49 +00:00
fix missing AABB expansion for non-AO faces
This commit is contained in:
parent
3a78d5f334
commit
0eec97726d
2 changed files with 446 additions and 421 deletions
|
|
@ -245,10 +245,17 @@ void BlocksRenderer::face(
|
||||||
tint *= d;
|
tint *= d;
|
||||||
}
|
}
|
||||||
const auto nZ2 = lights ? nZ : Z;
|
const auto nZ2 = lights ? nZ : Z;
|
||||||
vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, tint, nZ2, lights ? 0 : 1);
|
auto p0 = coord + (-X - Y + Z) * s;
|
||||||
vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, tint, nZ2, lights ? 0 : 1);
|
auto p1 = coord + ( X - Y + Z) * s;
|
||||||
vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, tint, nZ2, lights ? 0 : 1);
|
auto p2 = coord + ( X + Y + Z) * s;
|
||||||
vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, tint, nZ2, lights ? 0 : 1);
|
auto p3 = coord + (-X + Y + Z) * s;
|
||||||
|
vertex(p0, region.u1, region.v1, tint, nZ2, lights ? 0 : 1);
|
||||||
|
vertex(p1, region.u2, region.v1, tint, nZ2, lights ? 0 : 1);
|
||||||
|
vertex(p2, region.u2, region.v2, tint, nZ2, lights ? 0 : 1);
|
||||||
|
vertex(p3, region.u1, region.v2, tint, nZ2, lights ? 0 : 1);
|
||||||
|
if (!densePass) {
|
||||||
|
expand_aabb_4(localAabb, localAabbInit, p0, p1, p2, p3);
|
||||||
|
}
|
||||||
index(0, 1, 2, 0, 2, 3);
|
index(0, 1, 2, 0, 2, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,417 +1,435 @@
|
||||||
#include "ChunksRenderer.hpp"
|
#include "ChunksRenderer.hpp"
|
||||||
#include "BlocksRenderer.hpp"
|
#include "BlocksRenderer.hpp"
|
||||||
#include "debug/Logger.hpp"
|
#include "debug/Logger.hpp"
|
||||||
#include "assets/Assets.hpp"
|
#include "assets/Assets.hpp"
|
||||||
#include "graphics/core/Mesh.hpp"
|
#include "graphics/core/Mesh.hpp"
|
||||||
#include "graphics/core/Shader.hpp"
|
#include "graphics/core/Shader.hpp"
|
||||||
#include "graphics/core/Texture.hpp"
|
#include "graphics/core/Texture.hpp"
|
||||||
#include "graphics/core/Atlas.hpp"
|
#include "graphics/core/Atlas.hpp"
|
||||||
#include "voxels/Chunk.hpp"
|
#include "voxels/Chunk.hpp"
|
||||||
#include "voxels/Chunks.hpp"
|
#include "voxels/Chunks.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "window/Camera.hpp"
|
#include "window/Camera.hpp"
|
||||||
#include "maths/FrustumCulling.hpp"
|
#include "maths/FrustumCulling.hpp"
|
||||||
#include "util/listutil.hpp"
|
#include "util/listutil.hpp"
|
||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
static debug::Logger logger("chunks-render");
|
static debug::Logger logger("chunks-render");
|
||||||
|
|
||||||
size_t ChunksRenderer::visibleChunks = 0;
|
size_t ChunksRenderer::visibleChunks = 0;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
struct CullingBounds { glm::vec3 min; glm::vec3 max; };
|
struct CullingBounds { glm::vec3 min; glm::vec3 max; };
|
||||||
static constexpr float K_CHUNK_CENTER_BIAS = 0.5f;
|
static constexpr float K_CHUNK_CENTER_BIAS = 0.5f;
|
||||||
|
// Minimal thickness to avoid culling flicker for geometry that forms 2D sheets
|
||||||
static inline bool has_volume(const AABB& aabb) {
|
static constexpr float K_AABB_MIN_EXTENT = 1e-2f;
|
||||||
auto s = aabb.size();
|
|
||||||
return s.x > 0.0f || s.y > 0.0f || s.z > 0.0f;
|
static inline bool has_volume(const AABB& aabb) {
|
||||||
}
|
auto s = aabb.size();
|
||||||
|
return s.x > 0.0f || s.y > 0.0f || s.z > 0.0f;
|
||||||
static inline CullingBounds compute_chunk_culling_bounds(
|
}
|
||||||
const Chunk& chunk,
|
|
||||||
const std::unordered_map<glm::ivec2, ChunkMesh>& meshes
|
static inline CullingBounds compute_chunk_culling_bounds(
|
||||||
) {
|
const Chunk& chunk,
|
||||||
glm::vec3 min(chunk.x * CHUNK_W, chunk.bottom, chunk.z * CHUNK_D);
|
const std::unordered_map<glm::ivec2, ChunkMesh>& meshes
|
||||||
glm::vec3 max(
|
) {
|
||||||
chunk.x * CHUNK_W + CHUNK_W,
|
glm::vec3 min(chunk.x * CHUNK_W, chunk.bottom, chunk.z * CHUNK_D);
|
||||||
chunk.top,
|
glm::vec3 max(
|
||||||
chunk.z * CHUNK_D + CHUNK_D
|
chunk.x * CHUNK_W + CHUNK_W,
|
||||||
);
|
chunk.top,
|
||||||
auto it = meshes.find({chunk.x, chunk.z});
|
chunk.z * CHUNK_D + CHUNK_D
|
||||||
if (it != meshes.end()) {
|
);
|
||||||
const auto& aabb = it->second.localAabb;
|
auto it = meshes.find({chunk.x, chunk.z});
|
||||||
if (has_volume(aabb)) {
|
if (it != meshes.end()) {
|
||||||
min = glm::vec3(chunk.x * CHUNK_W + aabb.min().x + K_CHUNK_CENTER_BIAS,
|
const auto& aabb = it->second.localAabb;
|
||||||
(std::max)(static_cast<float>(chunk.bottom), aabb.min().y + K_CHUNK_CENTER_BIAS),
|
if (has_volume(aabb)) {
|
||||||
chunk.z * CHUNK_D + aabb.min().z + K_CHUNK_CENTER_BIAS);
|
// Convert to world coords (same 0.5 bias as draw model matrix)
|
||||||
max = glm::vec3(chunk.x * CHUNK_W + aabb.max().x + K_CHUNK_CENTER_BIAS,
|
min = glm::vec3(chunk.x * CHUNK_W + aabb.min().x + K_CHUNK_CENTER_BIAS,
|
||||||
(std::min)(static_cast<float>(chunk.top), aabb.max().y + K_CHUNK_CENTER_BIAS),
|
aabb.min().y + K_CHUNK_CENTER_BIAS,
|
||||||
chunk.z * CHUNK_D + aabb.max().z + K_CHUNK_CENTER_BIAS);
|
chunk.z * CHUNK_D + aabb.min().z + K_CHUNK_CENTER_BIAS);
|
||||||
}
|
max = glm::vec3(chunk.x * CHUNK_W + aabb.max().x + K_CHUNK_CENTER_BIAS,
|
||||||
}
|
aabb.max().y + K_CHUNK_CENTER_BIAS,
|
||||||
return {min, max};
|
chunk.z * CHUNK_D + aabb.max().z + K_CHUNK_CENTER_BIAS);
|
||||||
}
|
|
||||||
}
|
// Clamp vertically to chunk vertical span to keep bounds tight and valid
|
||||||
|
min.y = (std::max)(static_cast<float>(chunk.bottom), min.y);
|
||||||
class RendererWorker : public util::Worker<std::shared_ptr<Chunk>, RendererResult> {
|
max.y = (std::min)(static_cast<float>(chunk.top), max.y);
|
||||||
const Chunks& chunks;
|
|
||||||
BlocksRenderer renderer;
|
// Ensure non-degenerate extents to avoid view-dependent flicker
|
||||||
public:
|
glm::vec3 size = max - min;
|
||||||
RendererWorker(
|
auto inflate_axis = [&](int axis) {
|
||||||
const Level& level,
|
float c = (min[axis] + max[axis]) * 0.5f;
|
||||||
const Chunks& chunks,
|
min[axis] = c - K_AABB_MIN_EXTENT * 0.5f;
|
||||||
const ContentGfxCache& cache,
|
max[axis] = c + K_AABB_MIN_EXTENT * 0.5f;
|
||||||
const EngineSettings& settings
|
};
|
||||||
)
|
if (size.x < K_AABB_MIN_EXTENT) inflate_axis(0);
|
||||||
: chunks(chunks),
|
if (size.y < K_AABB_MIN_EXTENT) inflate_axis(1);
|
||||||
renderer(
|
if (size.z < K_AABB_MIN_EXTENT) inflate_axis(2);
|
||||||
settings.graphics.denseRender.get()
|
}
|
||||||
? settings.graphics.chunkMaxVerticesDense.get()
|
}
|
||||||
: settings.graphics.chunkMaxVertices.get(),
|
return {min, max};
|
||||||
level.content,
|
}
|
||||||
cache,
|
}
|
||||||
settings
|
|
||||||
) {
|
class RendererWorker : public util::Worker<std::shared_ptr<Chunk>, RendererResult> {
|
||||||
}
|
const Chunks& chunks;
|
||||||
|
BlocksRenderer renderer;
|
||||||
RendererResult operator()(const std::shared_ptr<Chunk>& chunk) override {
|
public:
|
||||||
renderer.build(chunk.get(), &chunks);
|
RendererWorker(
|
||||||
if (renderer.isCancelled()) {
|
const Level& level,
|
||||||
return RendererResult {
|
const Chunks& chunks,
|
||||||
glm::ivec2(chunk->x, chunk->z), true, ChunkMeshData {}};
|
const ContentGfxCache& cache,
|
||||||
}
|
const EngineSettings& settings
|
||||||
auto meshData = renderer.createMesh();
|
)
|
||||||
return RendererResult {
|
: chunks(chunks),
|
||||||
glm::ivec2(chunk->x, chunk->z), false, std::move(meshData)};
|
renderer(
|
||||||
}
|
settings.graphics.denseRender.get()
|
||||||
};
|
? settings.graphics.chunkMaxVerticesDense.get()
|
||||||
|
: settings.graphics.chunkMaxVertices.get(),
|
||||||
ChunksRenderer::ChunksRenderer(
|
level.content,
|
||||||
const Level* level,
|
cache,
|
||||||
const Chunks& chunks,
|
settings
|
||||||
const Assets& assets,
|
) {
|
||||||
const Frustum& frustum,
|
}
|
||||||
const ContentGfxCache& cache,
|
|
||||||
const EngineSettings& settings
|
RendererResult operator()(const std::shared_ptr<Chunk>& chunk) override {
|
||||||
)
|
renderer.build(chunk.get(), &chunks);
|
||||||
: chunks(chunks),
|
if (renderer.isCancelled()) {
|
||||||
assets(assets),
|
return RendererResult {
|
||||||
frustum(frustum),
|
glm::ivec2(chunk->x, chunk->z), true, ChunkMeshData {}};
|
||||||
settings(settings),
|
}
|
||||||
threadPool(
|
auto meshData = renderer.createMesh();
|
||||||
"chunks-render-pool",
|
return RendererResult {
|
||||||
[&]() {
|
glm::ivec2(chunk->x, chunk->z), false, std::move(meshData)};
|
||||||
return std::make_shared<RendererWorker>(
|
}
|
||||||
*level, chunks, cache, settings
|
};
|
||||||
);
|
|
||||||
},
|
ChunksRenderer::ChunksRenderer(
|
||||||
[&](RendererResult& result) {
|
const Level* level,
|
||||||
if (!result.cancelled) {
|
const Chunks& chunks,
|
||||||
auto meshData = std::move(result.meshData);
|
const Assets& assets,
|
||||||
meshes[result.key] = ChunkMesh {
|
const Frustum& frustum,
|
||||||
std::make_unique<Mesh<ChunkVertex>>(meshData.mesh),
|
const ContentGfxCache& cache,
|
||||||
std::move(meshData.sortingMesh)};
|
const EngineSettings& settings
|
||||||
meshes[result.key].localAabb = meshData.localAabb;
|
)
|
||||||
}
|
: chunks(chunks),
|
||||||
inwork.erase(result.key);
|
assets(assets),
|
||||||
},
|
frustum(frustum),
|
||||||
settings.graphics.chunkMaxRenderers.get()
|
settings(settings),
|
||||||
) {
|
threadPool(
|
||||||
threadPool.setStopOnFail(false);
|
"chunks-render-pool",
|
||||||
renderer = std::make_unique<BlocksRenderer>(
|
[&]() {
|
||||||
settings.graphics.chunkMaxVertices.get(),
|
return std::make_shared<RendererWorker>(
|
||||||
level->content, cache, settings
|
*level, chunks, cache, settings
|
||||||
);
|
);
|
||||||
logger.info() << "created " << threadPool.getWorkersCount() << " workers";
|
},
|
||||||
logger.info() << "memory consumption is "
|
[&](RendererResult& result) {
|
||||||
<< renderer->getMemoryConsumption() * threadPool.getWorkersCount()
|
if (!result.cancelled) {
|
||||||
<< " B";
|
auto meshData = std::move(result.meshData);
|
||||||
}
|
meshes[result.key] = ChunkMesh {
|
||||||
|
std::make_unique<Mesh<ChunkVertex>>(meshData.mesh),
|
||||||
ChunksRenderer::~ChunksRenderer() = default;
|
std::move(meshData.sortingMesh)};
|
||||||
|
meshes[result.key].localAabb = meshData.localAabb;
|
||||||
const Mesh<ChunkVertex>* ChunksRenderer::render(
|
}
|
||||||
const std::shared_ptr<Chunk>& chunk, bool important
|
inwork.erase(result.key);
|
||||||
) {
|
},
|
||||||
chunk->flags.modified = false;
|
settings.graphics.chunkMaxRenderers.get()
|
||||||
if (important) {
|
) {
|
||||||
auto mesh = renderer->render(chunk.get(), &chunks);
|
threadPool.setStopOnFail(false);
|
||||||
meshes[glm::ivec2(chunk->x, chunk->z)] = ChunkMesh {
|
renderer = std::make_unique<BlocksRenderer>(
|
||||||
std::move(mesh.mesh), std::move(mesh.sortingMeshData)
|
settings.graphics.chunkMaxVertices.get(),
|
||||||
};
|
level->content, cache, settings
|
||||||
// propagate local aabb from immediate path too
|
);
|
||||||
meshes[glm::ivec2(chunk->x, chunk->z)].localAabb = renderer->getLocalAabb();
|
logger.info() << "created " << threadPool.getWorkersCount() << " workers";
|
||||||
return meshes[glm::ivec2(chunk->x, chunk->z)].mesh.get();
|
logger.info() << "memory consumption is "
|
||||||
}
|
<< renderer->getMemoryConsumption() * threadPool.getWorkersCount()
|
||||||
glm::ivec2 key(chunk->x, chunk->z);
|
<< " B";
|
||||||
if (inwork.find(key) != inwork.end()) {
|
}
|
||||||
return nullptr;
|
|
||||||
}
|
ChunksRenderer::~ChunksRenderer() = default;
|
||||||
inwork[key] = true;
|
|
||||||
threadPool.enqueueJob(chunk);
|
const Mesh<ChunkVertex>* ChunksRenderer::render(
|
||||||
return nullptr;
|
const std::shared_ptr<Chunk>& chunk, bool important
|
||||||
}
|
) {
|
||||||
|
chunk->flags.modified = false;
|
||||||
void ChunksRenderer::unload(const Chunk* chunk) {
|
if (important) {
|
||||||
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
auto mesh = renderer->render(chunk.get(), &chunks);
|
||||||
if (found != meshes.end()) {
|
meshes[glm::ivec2(chunk->x, chunk->z)] = ChunkMesh {
|
||||||
meshes.erase(found);
|
std::move(mesh.mesh), std::move(mesh.sortingMeshData)
|
||||||
}
|
};
|
||||||
}
|
// propagate local aabb from immediate path too
|
||||||
|
meshes[glm::ivec2(chunk->x, chunk->z)].localAabb = renderer->getLocalAabb();
|
||||||
void ChunksRenderer::clear() {
|
return meshes[glm::ivec2(chunk->x, chunk->z)].mesh.get();
|
||||||
meshes.clear();
|
}
|
||||||
inwork.clear();
|
glm::ivec2 key(chunk->x, chunk->z);
|
||||||
threadPool.clearQueue();
|
if (inwork.find(key) != inwork.end()) {
|
||||||
}
|
return nullptr;
|
||||||
|
}
|
||||||
const Mesh<ChunkVertex>* ChunksRenderer::getOrRender(
|
inwork[key] = true;
|
||||||
const std::shared_ptr<Chunk>& chunk, bool important
|
threadPool.enqueueJob(chunk);
|
||||||
) {
|
return nullptr;
|
||||||
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
}
|
||||||
if (found == meshes.end()) {
|
|
||||||
return render(chunk, important);
|
void ChunksRenderer::unload(const Chunk* chunk) {
|
||||||
}
|
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
||||||
if (chunk->flags.modified && chunk->flags.lighted) {
|
if (found != meshes.end()) {
|
||||||
render(chunk, important);
|
meshes.erase(found);
|
||||||
}
|
}
|
||||||
return found->second.mesh.get();
|
}
|
||||||
}
|
|
||||||
|
void ChunksRenderer::clear() {
|
||||||
void ChunksRenderer::update() {
|
meshes.clear();
|
||||||
threadPool.update();
|
inwork.clear();
|
||||||
}
|
threadPool.clearQueue();
|
||||||
|
}
|
||||||
const Mesh<ChunkVertex>* ChunksRenderer::retrieveChunk(
|
|
||||||
size_t index, const Camera& camera, bool culling
|
const Mesh<ChunkVertex>* ChunksRenderer::getOrRender(
|
||||||
) {
|
const std::shared_ptr<Chunk>& chunk, bool important
|
||||||
auto chunk = chunks.getChunks()[index];
|
) {
|
||||||
if (chunk == nullptr) {
|
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
||||||
return nullptr;
|
if (found == meshes.end()) {
|
||||||
}
|
return render(chunk, important);
|
||||||
if (!chunk->flags.lighted) {
|
}
|
||||||
const auto& found = meshes.find({chunk->x, chunk->z});
|
if (chunk->flags.modified && chunk->flags.lighted) {
|
||||||
if (found == meshes.end()) {
|
render(chunk, important);
|
||||||
return nullptr;
|
}
|
||||||
} else {
|
return found->second.mesh.get();
|
||||||
return found->second.mesh.get();
|
}
|
||||||
}
|
|
||||||
}
|
void ChunksRenderer::update() {
|
||||||
float distance = glm::distance(
|
threadPool.update();
|
||||||
camera.position,
|
}
|
||||||
glm::vec3(
|
|
||||||
(chunk->x + 0.5f) * CHUNK_W,
|
const Mesh<ChunkVertex>* ChunksRenderer::retrieveChunk(
|
||||||
camera.position.y,
|
size_t index, const Camera& camera, bool culling
|
||||||
(chunk->z + 0.5f) * CHUNK_D
|
) {
|
||||||
)
|
auto chunk = chunks.getChunks()[index];
|
||||||
);
|
if (chunk == nullptr) {
|
||||||
auto mesh = getOrRender(chunk, distance < CHUNK_W * 1.5f);
|
return nullptr;
|
||||||
if (mesh == nullptr) {
|
}
|
||||||
return nullptr;
|
if (!chunk->flags.lighted) {
|
||||||
}
|
const auto& found = meshes.find({chunk->x, chunk->z});
|
||||||
if (chunk->flags.dirtyHeights) {
|
if (found == meshes.end()) {
|
||||||
chunk->updateHeights();
|
return nullptr;
|
||||||
}
|
} else {
|
||||||
if (culling) {
|
return found->second.mesh.get();
|
||||||
const auto bounds = compute_chunk_culling_bounds(*chunk, meshes);
|
}
|
||||||
if (!frustum.isBoxVisible(bounds.min, bounds.max)) return nullptr;
|
}
|
||||||
}
|
float distance = glm::distance(
|
||||||
return mesh;
|
camera.position,
|
||||||
}
|
glm::vec3(
|
||||||
|
(chunk->x + 0.5f) * CHUNK_W,
|
||||||
void ChunksRenderer::drawShadowsPass(
|
camera.position.y,
|
||||||
const Camera& camera, Shader& shader, const Camera& playerCamera
|
(chunk->z + 0.5f) * CHUNK_D
|
||||||
) {
|
)
|
||||||
Frustum frustum;
|
);
|
||||||
frustum.update(camera.getProjView());
|
auto mesh = getOrRender(chunk, distance < CHUNK_W * 1.5f);
|
||||||
|
if (mesh == nullptr) {
|
||||||
const auto& atlas = assets.require<Atlas>("blocks");
|
return nullptr;
|
||||||
|
}
|
||||||
atlas.getTexture()->bind();
|
if (chunk->flags.dirtyHeights) {
|
||||||
|
chunk->updateHeights();
|
||||||
auto denseDistance = settings.graphics.denseRenderDistance.get();
|
}
|
||||||
auto denseDistance2 = denseDistance * denseDistance;
|
if (culling) {
|
||||||
|
const auto bounds = compute_chunk_culling_bounds(*chunk, meshes);
|
||||||
for (const auto& chunk : chunks.getChunks()) {
|
if (!frustum.isBoxVisible(bounds.min, bounds.max)) return nullptr;
|
||||||
if (chunk == nullptr) {
|
}
|
||||||
continue;
|
return mesh;
|
||||||
}
|
}
|
||||||
glm::ivec2 pos {chunk->x, chunk->z};
|
|
||||||
const auto& found = meshes.find({chunk->x, chunk->z});
|
void ChunksRenderer::drawShadowsPass(
|
||||||
if (found == meshes.end()) {
|
const Camera& camera, Shader& shader, const Camera& playerCamera
|
||||||
continue;
|
) {
|
||||||
}
|
Frustum frustum;
|
||||||
|
frustum.update(camera.getProjView());
|
||||||
glm::vec3 coord(
|
|
||||||
pos.x * CHUNK_W + K_CHUNK_CENTER_BIAS, K_CHUNK_CENTER_BIAS, pos.y * CHUNK_D + K_CHUNK_CENTER_BIAS
|
const auto& atlas = assets.require<Atlas>("blocks");
|
||||||
);
|
|
||||||
|
atlas.getTexture()->bind();
|
||||||
const auto bounds = compute_chunk_culling_bounds(*chunk, meshes);
|
|
||||||
if (!frustum.isBoxVisible(bounds.min, bounds.max)) {
|
auto denseDistance = settings.graphics.denseRenderDistance.get();
|
||||||
continue;
|
auto denseDistance2 = denseDistance * denseDistance;
|
||||||
}
|
|
||||||
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
|
for (const auto& chunk : chunks.getChunks()) {
|
||||||
shader.uniformMatrix("u_model", model);
|
if (chunk == nullptr) {
|
||||||
found->second.mesh->draw(GL_TRIANGLES,
|
continue;
|
||||||
glm::distance2(playerCamera.position * glm::vec3(1, 0, 1),
|
}
|
||||||
(bounds.min + bounds.max) * 0.5f * glm::vec3(1, 0, 1)) < denseDistance2);
|
glm::ivec2 pos {chunk->x, chunk->z};
|
||||||
}
|
const auto& found = meshes.find({chunk->x, chunk->z});
|
||||||
}
|
if (found == meshes.end()) {
|
||||||
|
continue;
|
||||||
void ChunksRenderer::drawChunks(
|
}
|
||||||
const Camera& camera, Shader& shader
|
|
||||||
) {
|
glm::vec3 coord(
|
||||||
const auto& atlas = assets.require<Atlas>("blocks");
|
pos.x * CHUNK_W + K_CHUNK_CENTER_BIAS, K_CHUNK_CENTER_BIAS, pos.y * CHUNK_D + K_CHUNK_CENTER_BIAS
|
||||||
|
);
|
||||||
atlas.getTexture()->bind();
|
|
||||||
|
const auto bounds = compute_chunk_culling_bounds(*chunk, meshes);
|
||||||
// [warning] this whole method is not thread-safe for chunks
|
if (!frustum.isBoxVisible(bounds.min, bounds.max)) {
|
||||||
|
continue;
|
||||||
int chunksWidth = chunks.getWidth();
|
}
|
||||||
int chunksOffsetX = chunks.getOffsetX();
|
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
|
||||||
int chunksOffsetY = chunks.getOffsetY();
|
shader.uniformMatrix("u_model", model);
|
||||||
|
found->second.mesh->draw(GL_TRIANGLES,
|
||||||
if (indices.size() != chunks.getVolume()) {
|
glm::distance2(playerCamera.position * glm::vec3(1, 0, 1),
|
||||||
indices.clear();
|
(bounds.min + bounds.max) * 0.5f * glm::vec3(1, 0, 1)) < denseDistance2);
|
||||||
for (int i = 0; i < chunks.getVolume(); i++) {
|
}
|
||||||
indices.push_back(ChunksSortEntry {i, 0});
|
}
|
||||||
}
|
|
||||||
}
|
void ChunksRenderer::drawChunks(
|
||||||
float px = camera.position.x / static_cast<float>(CHUNK_W) - 0.5f;
|
const Camera& camera, Shader& shader
|
||||||
float pz = camera.position.z / static_cast<float>(CHUNK_D) - 0.5f;
|
) {
|
||||||
for (auto& index : indices) {
|
const auto& atlas = assets.require<Atlas>("blocks");
|
||||||
float x = index.index % chunksWidth + chunksOffsetX - px;
|
|
||||||
float z = index.index / chunksWidth + chunksOffsetY - pz;
|
atlas.getTexture()->bind();
|
||||||
index.d = (x * x + z * z) * 1024;
|
|
||||||
}
|
// [warning] this whole method is not thread-safe for chunks
|
||||||
util::insertion_sort(indices.begin(), indices.end());
|
|
||||||
|
int chunksWidth = chunks.getWidth();
|
||||||
bool culling = settings.graphics.frustumCulling.get();
|
int chunksOffsetX = chunks.getOffsetX();
|
||||||
|
int chunksOffsetY = chunks.getOffsetY();
|
||||||
visibleChunks = 0;
|
|
||||||
shader.uniform1i("u_alphaClip", true);
|
if (indices.size() != chunks.getVolume()) {
|
||||||
|
indices.clear();
|
||||||
auto denseDistance = settings.graphics.denseRenderDistance.get();
|
for (int i = 0; i < chunks.getVolume(); i++) {
|
||||||
auto denseDistance2 = denseDistance * denseDistance;
|
indices.push_back(ChunksSortEntry {i, 0});
|
||||||
|
}
|
||||||
// TODO: minimize draw calls number
|
}
|
||||||
for (int i = indices.size()-1; i >= 0; i--) {
|
float px = camera.position.x / static_cast<float>(CHUNK_W) - 0.5f;
|
||||||
auto& chunk = chunks.getChunks()[indices[i].index];
|
float pz = camera.position.z / static_cast<float>(CHUNK_D) - 0.5f;
|
||||||
auto mesh = retrieveChunk(indices[i].index, camera, culling);
|
for (auto& index : indices) {
|
||||||
|
float x = index.index % chunksWidth + chunksOffsetX - px;
|
||||||
if (mesh) {
|
float z = index.index / chunksWidth + chunksOffsetY - pz;
|
||||||
glm::vec3 coord(
|
index.d = (x * x + z * z) * 1024;
|
||||||
chunk->x * CHUNK_W + K_CHUNK_CENTER_BIAS, K_CHUNK_CENTER_BIAS, chunk->z * CHUNK_D + K_CHUNK_CENTER_BIAS
|
}
|
||||||
);
|
util::insertion_sort(indices.begin(), indices.end());
|
||||||
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
|
|
||||||
shader.uniformMatrix("u_model", model);
|
bool culling = settings.graphics.frustumCulling.get();
|
||||||
mesh->draw(GL_TRIANGLES, glm::distance2(camera.position * glm::vec3(1, 0, 1),
|
|
||||||
(coord + glm::vec3(CHUNK_W * 0.5f, 0.0f, CHUNK_D * 0.5f))) < denseDistance2);
|
visibleChunks = 0;
|
||||||
visibleChunks++;
|
shader.uniform1i("u_alphaClip", true);
|
||||||
}
|
|
||||||
}
|
auto denseDistance = settings.graphics.denseRenderDistance.get();
|
||||||
}
|
auto denseDistance2 = denseDistance * denseDistance;
|
||||||
|
|
||||||
static inline void write_sorting_mesh_entries(
|
// TODO: minimize draw calls number
|
||||||
ChunkVertex* buffer, const std::vector<SortingMeshEntry>& chunkEntries
|
for (int i = indices.size()-1; i >= 0; i--) {
|
||||||
) {
|
auto& chunk = chunks.getChunks()[indices[i].index];
|
||||||
for (const auto& entry : chunkEntries) {
|
auto mesh = retrieveChunk(indices[i].index, camera, culling);
|
||||||
const auto& vertexData = entry.vertexData;
|
|
||||||
std::memcpy(
|
if (mesh) {
|
||||||
buffer,
|
glm::vec3 coord(
|
||||||
vertexData.data(),
|
chunk->x * CHUNK_W + K_CHUNK_CENTER_BIAS, K_CHUNK_CENTER_BIAS, chunk->z * CHUNK_D + K_CHUNK_CENTER_BIAS
|
||||||
vertexData.size() * sizeof(ChunkVertex)
|
);
|
||||||
);
|
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
|
||||||
buffer += vertexData.size();
|
shader.uniformMatrix("u_model", model);
|
||||||
}
|
mesh->draw(GL_TRIANGLES, glm::distance2(camera.position * glm::vec3(1, 0, 1),
|
||||||
}
|
(coord + glm::vec3(CHUNK_W * 0.5f, 0.0f, CHUNK_D * 0.5f))) < denseDistance2);
|
||||||
|
visibleChunks++;
|
||||||
void ChunksRenderer::drawSortedMeshes(const Camera& camera, Shader& shader) {
|
}
|
||||||
const int sortInterval = TRANSLUCENT_BLOCKS_SORT_INTERVAL;
|
}
|
||||||
static int frameid = 0;
|
}
|
||||||
frameid++;
|
|
||||||
|
static inline void write_sorting_mesh_entries(
|
||||||
const bool culling = settings.graphics.frustumCulling.get();
|
ChunkVertex* buffer, const std::vector<SortingMeshEntry>& chunkEntries
|
||||||
const auto& chunks = this->chunks.getChunks();
|
) {
|
||||||
const auto& cameraPos = camera.position;
|
for (const auto& entry : chunkEntries) {
|
||||||
const auto& atlas = assets.require<Atlas>("blocks");
|
const auto& vertexData = entry.vertexData;
|
||||||
|
std::memcpy(
|
||||||
shader.use();
|
buffer,
|
||||||
atlas.getTexture()->bind();
|
vertexData.data(),
|
||||||
shader.uniformMatrix("u_model", glm::mat4(1.0f));
|
vertexData.size() * sizeof(ChunkVertex)
|
||||||
shader.uniform1i("u_alphaClip", false);
|
);
|
||||||
|
buffer += vertexData.size();
|
||||||
struct VisibleChunkTrans {
|
}
|
||||||
glm::ivec2 key;
|
}
|
||||||
const std::shared_ptr<Chunk>* chunkPtr;
|
|
||||||
long long nearestDist2;
|
void ChunksRenderer::drawSortedMeshes(const Camera& camera, Shader& shader) {
|
||||||
};
|
const int sortInterval = TRANSLUCENT_BLOCKS_SORT_INTERVAL;
|
||||||
std::vector<VisibleChunkTrans> order;
|
static int frameid = 0;
|
||||||
order.reserve(indices.size());
|
frameid++;
|
||||||
|
|
||||||
// Build per-chunk nearest distance for translucent entries
|
const bool culling = settings.graphics.frustumCulling.get();
|
||||||
for (const auto& index : indices) {
|
const auto& chunks = this->chunks.getChunks();
|
||||||
const auto& chunk = chunks[index.index];
|
const auto& cameraPos = camera.position;
|
||||||
if (chunk == nullptr || !chunk->flags.lighted) {
|
const auto& atlas = assets.require<Atlas>("blocks");
|
||||||
continue;
|
|
||||||
}
|
shader.use();
|
||||||
const auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
atlas.getTexture()->bind();
|
||||||
if (found == meshes.end()) {
|
shader.uniformMatrix("u_model", glm::mat4(1.0f));
|
||||||
continue;
|
shader.uniform1i("u_alphaClip", false);
|
||||||
}
|
|
||||||
const auto& entries = found->second.sortingMeshData.entries;
|
struct VisibleChunkTrans {
|
||||||
if (entries.empty()) {
|
glm::ivec2 key;
|
||||||
continue;
|
const std::shared_ptr<Chunk>* chunkPtr;
|
||||||
}
|
long long nearestDist2;
|
||||||
|
};
|
||||||
if (culling) {
|
std::vector<VisibleChunkTrans> order;
|
||||||
const auto bounds = compute_chunk_culling_bounds(*chunk, meshes);
|
order.reserve(indices.size());
|
||||||
if (!frustum.isBoxVisible(bounds.min, bounds.max)) continue;
|
|
||||||
}
|
// Build per-chunk nearest distance for translucent entries
|
||||||
|
for (const auto& index : indices) {
|
||||||
long long nearest = LLONG_MAX;
|
const auto& chunk = chunks[index.index];
|
||||||
for (const auto& e : entries) {
|
if (chunk == nullptr || !chunk->flags.lighted) {
|
||||||
long long d2 = static_cast<long long>(glm::distance2(e.position, cameraPos));
|
continue;
|
||||||
if (d2 < nearest) nearest = d2;
|
}
|
||||||
}
|
const auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
||||||
order.push_back(VisibleChunkTrans{glm::ivec2(chunk->x, chunk->z), &chunks[index.index], nearest});
|
if (found == meshes.end()) {
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
// Sort chunks by nearest translucent distance back-to-front (far to near)
|
const auto& entries = found->second.sortingMeshData.entries;
|
||||||
std::sort(order.begin(), order.end(), [](const VisibleChunkTrans& a, const VisibleChunkTrans& b) {
|
if (entries.empty()) {
|
||||||
return a.nearestDist2 > b.nearestDist2;
|
continue;
|
||||||
});
|
}
|
||||||
|
|
||||||
// Draw per-chunk sorted mesh (keeps GPU buffers and avoids per-frame repack)
|
if (culling) {
|
||||||
for (const auto& item : order) {
|
const auto bounds = compute_chunk_culling_bounds(*chunk, meshes);
|
||||||
const auto& chunk = *item.chunkPtr;
|
if (!frustum.isBoxVisible(bounds.min, bounds.max)) continue;
|
||||||
const auto found = meshes.find(item.key);
|
}
|
||||||
if (found == meshes.end()) continue;
|
|
||||||
auto& chunkEntries = found->second.sortingMeshData.entries;
|
long long nearest = LLONG_MAX;
|
||||||
if (chunkEntries.empty()) continue;
|
for (const auto& e : entries) {
|
||||||
|
long long d2 = static_cast<long long>(glm::distance2(e.position, cameraPos));
|
||||||
// Keep per-chunk internal order up-to-date occasionally
|
if (d2 < nearest) nearest = d2;
|
||||||
if (found->second.sortedMesh == nullptr || (frameid + chunk->x) % sortInterval == 0) {
|
}
|
||||||
for (auto& entry : chunkEntries) {
|
order.push_back(VisibleChunkTrans{glm::ivec2(chunk->x, chunk->z), &chunks[index.index], nearest});
|
||||||
entry.distance = static_cast<long long>(
|
}
|
||||||
glm::distance2(entry.position, cameraPos)
|
|
||||||
);
|
// Sort chunks by nearest translucent distance back-to-front (far to near)
|
||||||
}
|
std::sort(order.begin(), order.end(), [](const VisibleChunkTrans& a, const VisibleChunkTrans& b) {
|
||||||
std::sort(chunkEntries.begin(), chunkEntries.end());
|
return a.nearestDist2 > b.nearestDist2;
|
||||||
size_t size = 0;
|
});
|
||||||
for (const auto& entry : chunkEntries) {
|
|
||||||
size += entry.vertexData.size();
|
// Draw per-chunk sorted mesh (keeps GPU buffers and avoids per-frame repack)
|
||||||
}
|
for (const auto& item : order) {
|
||||||
static util::Buffer<ChunkVertex> buffer;
|
const auto& chunk = *item.chunkPtr;
|
||||||
if (buffer.size() < size) {
|
const auto found = meshes.find(item.key);
|
||||||
buffer = util::Buffer<ChunkVertex>(size);
|
if (found == meshes.end()) continue;
|
||||||
}
|
auto& chunkEntries = found->second.sortingMeshData.entries;
|
||||||
write_sorting_mesh_entries(buffer.data(), chunkEntries);
|
if (chunkEntries.empty()) continue;
|
||||||
found->second.sortedMesh = std::make_unique<Mesh<ChunkVertex>>(
|
|
||||||
buffer.data(), size
|
// Keep per-chunk internal order up-to-date occasionally
|
||||||
);
|
if (found->second.sortedMesh == nullptr || (frameid + chunk->x) % sortInterval == 0) {
|
||||||
}
|
for (auto& entry : chunkEntries) {
|
||||||
found->second.sortedMesh->draw();
|
entry.distance = static_cast<long long>(
|
||||||
}
|
glm::distance2(entry.position, cameraPos)
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
std::sort(chunkEntries.begin(), chunkEntries.end());
|
||||||
|
size_t size = 0;
|
||||||
|
for (const auto& entry : chunkEntries) {
|
||||||
|
size += entry.vertexData.size();
|
||||||
|
}
|
||||||
|
static util::Buffer<ChunkVertex> buffer;
|
||||||
|
if (buffer.size() < size) {
|
||||||
|
buffer = util::Buffer<ChunkVertex>(size);
|
||||||
|
}
|
||||||
|
write_sorting_mesh_entries(buffer.data(), chunkEntries);
|
||||||
|
found->second.sortedMesh = std::make_unique<Mesh<ChunkVertex>>(
|
||||||
|
buffer.data(), size
|
||||||
|
);
|
||||||
|
}
|
||||||
|
found->second.sortedMesh->draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue