781 lines
27 KiB
C++
781 lines
27 KiB
C++
#include "BlocksRenderer.hpp"
|
|
|
|
#include "graphics/core/Mesh.hpp"
|
|
#include "graphics/commons/Model.hpp"
|
|
#include "maths/UVRegion.hpp"
|
|
#include "constants.hpp"
|
|
#include "content/Content.hpp"
|
|
#include "voxels/Chunks.hpp"
|
|
#include "lighting/Lightmap.hpp"
|
|
#include "frontend/ContentGfxCache.hpp"
|
|
|
|
const glm::vec3 BlocksRenderer::SUN_VECTOR(0.528265f, 0.833149f, -0.163704f);
|
|
const float DIRECTIONAL_LIGHT_FACTOR = 0.3f;
|
|
|
|
BlocksRenderer::BlocksRenderer(
|
|
size_t capacity,
|
|
const Content& content,
|
|
const ContentGfxCache& cache,
|
|
const EngineSettings& settings
|
|
) : content(content),
|
|
vertexBuffer(std::make_unique<ChunkVertex[]>(capacity)),
|
|
indexBuffer(std::make_unique<uint32_t[]>(capacity)),
|
|
denseIndexBuffer(std::make_unique<uint32_t[]>(capacity)),
|
|
vertexCount(0),
|
|
vertexOffset(0),
|
|
indexCount(0),
|
|
capacity(capacity),
|
|
cache(cache),
|
|
settings(settings)
|
|
{
|
|
voxelsBuffer = std::make_unique<VoxelsVolume>(
|
|
CHUNK_W + voxelBufferPadding*2,
|
|
CHUNK_H,
|
|
CHUNK_D + voxelBufferPadding*2);
|
|
blockDefsCache = content.getIndices()->blocks.getDefs();
|
|
}
|
|
|
|
BlocksRenderer::~BlocksRenderer() {
|
|
}
|
|
|
|
/// Basic vertex add method
|
|
void BlocksRenderer::vertex(
|
|
const glm::vec3& coord,
|
|
float u,
|
|
float v,
|
|
const glm::vec4& light,
|
|
const glm::vec3& normal,
|
|
float emission
|
|
) {
|
|
vertexBuffer[vertexCount].position = coord;
|
|
|
|
vertexBuffer[vertexCount].uv = {u,v};
|
|
|
|
vertexBuffer[vertexCount].normal[0] = static_cast<uint8_t>(normal.r * 127 + 128);
|
|
vertexBuffer[vertexCount].normal[1] = static_cast<uint8_t>(normal.g * 127 + 128);
|
|
vertexBuffer[vertexCount].normal[2] = static_cast<uint8_t>(normal.b * 127 + 128);
|
|
vertexBuffer[vertexCount].normal[3] = static_cast<uint8_t>(emission * 255);
|
|
|
|
vertexBuffer[vertexCount].color[0] = static_cast<uint8_t>(light.r * 255);
|
|
vertexBuffer[vertexCount].color[1] = static_cast<uint8_t>(light.g * 255);
|
|
vertexBuffer[vertexCount].color[2] = static_cast<uint8_t>(light.b * 255);
|
|
vertexBuffer[vertexCount].color[3] = static_cast<uint8_t>(light.a * 255);
|
|
|
|
vertexCount++;
|
|
}
|
|
|
|
void BlocksRenderer::index(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e, uint32_t f) {
|
|
indexBuffer[indexCount++] = static_cast<uint32_t>(vertexOffset + a);
|
|
indexBuffer[indexCount++] = static_cast<uint32_t>(vertexOffset + b);
|
|
indexBuffer[indexCount++] = static_cast<uint32_t>(vertexOffset + c);
|
|
indexBuffer[indexCount++] = static_cast<uint32_t>(vertexOffset + d);
|
|
indexBuffer[indexCount++] = static_cast<uint32_t>(vertexOffset + e);
|
|
indexBuffer[indexCount++] = static_cast<uint32_t>(vertexOffset + f);
|
|
vertexOffset += 4;
|
|
}
|
|
|
|
/// @brief Add face with precalculated lights
|
|
void BlocksRenderer::face(
|
|
const glm::vec3& coord,
|
|
float w, float h, float d,
|
|
const glm::vec3& axisX,
|
|
const glm::vec3& axisY,
|
|
const glm::vec3& axisZ,
|
|
const UVRegion& region,
|
|
const glm::vec4(&lights)[4],
|
|
const glm::vec4& tint
|
|
) {
|
|
if (vertexCount + 4 >= capacity) {
|
|
overflow = true;
|
|
return;
|
|
}
|
|
auto X = axisX * w;
|
|
auto Y = axisY * h;
|
|
auto Z = axisZ * d;
|
|
float s = 0.5f;
|
|
vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, lights[0] * tint, axisZ, 0);
|
|
vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, lights[1] * tint, axisZ, 0);
|
|
vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, lights[2] * tint, axisZ, 0);
|
|
vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, lights[3] * tint, axisZ, 0);
|
|
index(0, 1, 3, 1, 2, 3);
|
|
}
|
|
|
|
void BlocksRenderer::vertexAO(
|
|
const glm::vec3& coord,
|
|
float u, float v,
|
|
const glm::vec4& tint,
|
|
const glm::vec3& axisX,
|
|
const glm::vec3& axisY,
|
|
const glm::vec3& axisZ
|
|
) {
|
|
auto pos = coord+axisZ*0.5f+(axisX+axisY)*0.5f;
|
|
auto light = pickSoftLight(
|
|
glm::ivec3(std::round(pos.x), std::round(pos.y), std::round(pos.z)),
|
|
axisX,
|
|
axisY
|
|
);
|
|
vertex(coord, u, v, light * tint, axisZ, 0.0f);
|
|
}
|
|
|
|
void BlocksRenderer::faceAO(
|
|
const glm::vec3& coord,
|
|
const glm::vec3& X,
|
|
const glm::vec3& Y,
|
|
const glm::vec3& Z,
|
|
const UVRegion& region,
|
|
bool lights
|
|
) {
|
|
if (vertexCount + 4 >= capacity) {
|
|
overflow = true;
|
|
return;
|
|
}
|
|
|
|
float s = 0.5f;
|
|
if (lights) {
|
|
float d = glm::dot(glm::normalize(Z), SUN_VECTOR);
|
|
d = (1.0f - DIRECTIONAL_LIGHT_FACTOR) + d * DIRECTIONAL_LIGHT_FACTOR;
|
|
|
|
auto axisX = glm::normalize(X);
|
|
auto axisY = glm::normalize(Y);
|
|
auto axisZ = glm::normalize(Z);
|
|
|
|
glm::vec4 tint(d);
|
|
vertexAO(coord + (-X - Y + Z) * s, region.u1, region.v1, tint, axisX, axisY, axisZ);
|
|
vertexAO(coord + ( X - Y + Z) * s, region.u2, region.v1, tint, axisX, axisY, axisZ);
|
|
vertexAO(coord + ( X + Y + Z) * s, region.u2, region.v2, tint, axisX, axisY, axisZ);
|
|
vertexAO(coord + (-X + Y + Z) * s, region.u1, region.v2, tint, axisX, axisY, axisZ);
|
|
} else {
|
|
auto axisZ = glm::normalize(Z);
|
|
glm::vec4 tint(1.0f);
|
|
vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, tint, axisZ, 1);
|
|
vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, tint, axisZ, 1);
|
|
vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, tint, axisZ, 1);
|
|
vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, tint, axisZ, 1);
|
|
}
|
|
index(0, 1, 2, 0, 2, 3);
|
|
}
|
|
|
|
void BlocksRenderer::face(
|
|
const glm::vec3& coord,
|
|
const glm::vec3& X,
|
|
const glm::vec3& Y,
|
|
const glm::vec3& Z,
|
|
const UVRegion& region,
|
|
glm::vec4 tint,
|
|
bool lights
|
|
) {
|
|
if (vertexCount + 4 >= capacity) {
|
|
overflow = true;
|
|
return;
|
|
}
|
|
|
|
float s = 0.5f;
|
|
if (lights) {
|
|
float d = glm::dot(glm::normalize(Z), SUN_VECTOR);
|
|
d = (1.0f - DIRECTIONAL_LIGHT_FACTOR) + d * DIRECTIONAL_LIGHT_FACTOR;
|
|
tint *= d;
|
|
}
|
|
vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, tint, Z, lights ? 0 : 1);
|
|
vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, tint, Z, lights ? 0 : 1);
|
|
vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, tint, Z, lights ? 0 : 1);
|
|
vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, tint, Z, lights ? 0 : 1);
|
|
index(0, 1, 2, 0, 2, 3);
|
|
}
|
|
|
|
void BlocksRenderer::blockXSprite(
|
|
int x, int y, int z,
|
|
const glm::vec3& size,
|
|
const UVRegion& texface1,
|
|
const UVRegion& texface2,
|
|
float spread
|
|
) {
|
|
glm::vec4 lights1[] {
|
|
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})
|
|
};
|
|
glm::vec4 lights2[] {
|
|
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})
|
|
};
|
|
randomizer.setSeed((x * 52321) ^ (z * 389) ^ y);
|
|
short rand = randomizer.rand32();
|
|
|
|
float xs = ((float)(char)rand / 512) * spread;
|
|
float zs = ((float)(char)(rand >> 8) / 512) * spread;
|
|
|
|
const float w = size.x / 1.41f;
|
|
const glm::vec4 tint (0.8f);
|
|
|
|
glm::vec3 n(0.0f, 1.0f, 0.0f);
|
|
|
|
face({x + xs, y, z + zs}, w, size.y, 0, {-1, 0, 1}, {0, 1, 0}, n,
|
|
texface1, lights2, tint);
|
|
face({x + xs, y, z + zs}, w, size.y, 0, {1, 0, 1}, {0, 1, 0}, n,
|
|
texface1, lights1, tint);
|
|
|
|
face({x + xs, y, z + zs}, w, size.y, 0, {-1, 0, -1}, {0, 1, 0}, n,
|
|
texface2, lights2, tint);
|
|
face({x + xs, y, z + zs}, w, size.y, 0, {1, 0, -1}, {0, 1, 0}, n,
|
|
texface2, lights1, tint);
|
|
}
|
|
|
|
// HINT: texture faces order: {east, west, bottom, top, south, north}
|
|
|
|
/// @brief AABB blocks render method
|
|
void BlocksRenderer::blockAABB(
|
|
const glm::ivec3& icoord,
|
|
const UVRegion(&texfaces)[6],
|
|
const Block* block,
|
|
ubyte rotation,
|
|
bool lights,
|
|
bool ao
|
|
) {
|
|
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);
|
|
}
|
|
auto size = hitbox.size();
|
|
glm::vec3 X(1, 0, 0);
|
|
glm::vec3 Y(0, 1, 0);
|
|
glm::vec3 Z(0, 0, 1);
|
|
glm::vec3 coord(icoord);
|
|
if (block->rotatable) {
|
|
auto& rotations = block->rotations;
|
|
auto& orient = rotations.variants[rotation];
|
|
X = orient.axes[0];
|
|
Y = orient.axes[1];
|
|
Z = orient.axes[2];
|
|
orient.transform(hitbox);
|
|
}
|
|
coord -= glm::vec3(0.5f) - hitbox.center();
|
|
|
|
if (ao) {
|
|
faceAO(coord, X*size.x, Y*size.y, Z*size.z, texfaces[5], lights); // north
|
|
faceAO(coord, -X*size.x, Y*size.y, -Z*size.z, texfaces[4], lights); // south
|
|
|
|
faceAO(coord, X*size.x, -Z*size.z, Y*size.y, texfaces[3], lights); // top
|
|
faceAO(coord, -X*size.x, -Z*size.z, -Y*size.y, texfaces[2], lights); // bottom
|
|
|
|
faceAO(coord, -Z*size.z, Y*size.y, X*size.x, texfaces[1], lights); // west
|
|
faceAO(coord, Z*size.z, Y*size.y, -X*size.x, texfaces[0], lights); // east
|
|
} else {
|
|
auto tint = pickLight(icoord);
|
|
face(coord, X*size.x, Y*size.y, Z*size.z, texfaces[5], tint, lights); // north
|
|
face(coord, -X*size.x, Y*size.y, -Z*size.z, texfaces[4], tint, lights); // south
|
|
|
|
face(coord, X*size.x, -Z*size.z, Y*size.y, texfaces[3], tint, lights); // top
|
|
face(coord, -X*size.x, -Z*size.z, -Y*size.y, texfaces[2], tint, lights); // bottom
|
|
|
|
face(coord, -Z*size.z, Y*size.y, X*size.x, texfaces[1], tint, lights); // west
|
|
face(coord, Z*size.z, Y*size.y, -X*size.x, texfaces[0], tint, lights); // east
|
|
}
|
|
}
|
|
|
|
static bool is_aligned(const glm::vec3& v, float e = 1e-6f) {
|
|
if (std::abs(v.y) < e && std::abs(v.z) < e && std::abs(v.x) > e) {
|
|
return true;
|
|
}
|
|
if (std::abs(v.x) < e && std::abs(v.z) < e && std::abs(v.y) > e) {
|
|
return true;
|
|
}
|
|
if (std::abs(v.x) < e && std::abs(v.y) < e && std::abs(v.z) > e) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void BlocksRenderer::blockCustomModel(
|
|
const glm::ivec3& icoord, const Block& block, blockstate states, bool lights, bool ao
|
|
) {
|
|
const auto& variant = block.getVariantByBits(states.userbits);
|
|
glm::vec3 X(1, 0, 0);
|
|
glm::vec3 Y(0, 1, 0);
|
|
glm::vec3 Z(0, 0, 1);
|
|
glm::vec3 coord(icoord);
|
|
if (block.rotatable) {
|
|
auto& rotations = block.rotations;
|
|
CoordSystem orient = rotations.variants[states.rotation];
|
|
X = orient.axes[0];
|
|
Y = orient.axes[1];
|
|
Z = orient.axes[2];
|
|
}
|
|
|
|
const auto& model = cache.getModel(block.rt.id, block.getVariantIndex(states.userbits));
|
|
for (const auto& mesh : model.meshes) {
|
|
if (vertexCount + mesh.vertices.size() >= capacity) {
|
|
overflow = true;
|
|
return;
|
|
}
|
|
for (int triangle = 0; triangle < mesh.vertices.size() / 3; triangle++) {
|
|
auto r = mesh.vertices[triangle * 3 + (triangle % 2) * 2].coord -
|
|
mesh.vertices[triangle * 3 + 1].coord;
|
|
r = r.x * X + r.y * Y + r.z * Z;
|
|
r = glm::normalize(r);
|
|
|
|
const auto& v0 = mesh.vertices[triangle * 3];
|
|
auto n = v0.normal.x * X + v0.normal.y * Y + v0.normal.z * Z;
|
|
|
|
auto vp = (mesh.vertices[triangle * 3].coord +
|
|
mesh.vertices[triangle * 3 + 1].coord +
|
|
mesh.vertices[triangle * 3 + 2].coord) *
|
|
0.3333f -
|
|
0.5f;
|
|
vp = vp.x * X + vp.y * Y + vp.z * Z;
|
|
|
|
if (!isOpen(glm::floor(coord + vp + 0.5f + n * 1e-3f), block, variant) && is_aligned(n)) {
|
|
continue;
|
|
}
|
|
|
|
float d = glm::dot(n, SUN_VECTOR);
|
|
d = (1.0f - DIRECTIONAL_LIGHT_FACTOR) + d * DIRECTIONAL_LIGHT_FACTOR;
|
|
glm::vec3 t = glm::cross(r, n);
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
const auto& vertex = mesh.vertices[triangle * 3 + i];
|
|
const auto& vcoord = vertex.coord - 0.5f;
|
|
|
|
glm::vec4 aoColor {1.0f, 1.0f, 1.0f, 1.0f};
|
|
if (mesh.shading && ao) {
|
|
auto p = coord + vcoord.x * X + vcoord.y * Y +
|
|
vcoord.z * Z + r * 0.5f + t * 0.5f + n * 0.5f;
|
|
aoColor = pickSoftLight(p.x, p.y, p.z, glm::ivec3(r), glm::ivec3(t));
|
|
}
|
|
this->vertex(
|
|
coord + vcoord.x * X + vcoord.y * Y + vcoord.z * Z,
|
|
vertex.uv.x,
|
|
vertex.uv.y,
|
|
mesh.shading ? (glm::vec4(d, d, d, d) * aoColor) : glm::vec4(1, 1, 1, d),
|
|
n,
|
|
mesh.shading ? 0.0f : 1.0
|
|
);
|
|
indexBuffer[indexCount++] = vertexOffset++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Fastest solid shaded blocks render method */
|
|
void BlocksRenderer::blockCube(
|
|
const glm::ivec3& coord,
|
|
const UVRegion(&texfaces)[6],
|
|
const Block& block,
|
|
blockstate states,
|
|
bool lights,
|
|
bool ao
|
|
) {
|
|
const auto& variant = block.getVariantByBits(states.userbits);
|
|
glm::ivec3 X(1, 0, 0);
|
|
glm::ivec3 Y(0, 1, 0);
|
|
glm::ivec3 Z(0, 0, 1);
|
|
|
|
if (block.rotatable) {
|
|
auto& rotations = block.rotations;
|
|
auto& orient = rotations.variants[states.rotation];
|
|
X = orient.axes[0];
|
|
Y = orient.axes[1];
|
|
Z = orient.axes[2];
|
|
}
|
|
|
|
if (ao) {
|
|
if (isOpen(coord + Z, block, variant)) {
|
|
faceAO(coord, X, Y, Z, texfaces[5], lights);
|
|
}
|
|
if (isOpen(coord - Z, block, variant)) {
|
|
faceAO(coord, -X, Y, -Z, texfaces[4], lights);
|
|
}
|
|
if (isOpen(coord + Y, block, variant)) {
|
|
faceAO(coord, X, -Z, Y, texfaces[3], lights);
|
|
}
|
|
if (isOpen(coord - Y, block, variant)) {
|
|
faceAO(coord, X, Z, -Y, texfaces[2], lights);
|
|
}
|
|
if (isOpen(coord + X, block, variant)) {
|
|
faceAO(coord, -Z, Y, X, texfaces[1], lights);
|
|
}
|
|
if (isOpen(coord - X, block, variant)) {
|
|
faceAO(coord, Z, Y, -X, texfaces[0], lights);
|
|
}
|
|
} else {
|
|
if (isOpen(coord + Z, block, variant)) {
|
|
face(coord, X, Y, Z, texfaces[5], pickLight(coord + Z), lights);
|
|
}
|
|
if (isOpen(coord - Z, block, variant)) {
|
|
face(coord, -X, Y, -Z, texfaces[4], pickLight(coord - Z), lights);
|
|
}
|
|
if (isOpen(coord + Y, block, variant)) {
|
|
face(coord, X, -Z, Y, texfaces[3], pickLight(coord + Y), lights);
|
|
}
|
|
if (isOpen(coord - Y, block, variant)) {
|
|
face(coord, X, Z, -Y, texfaces[2], pickLight(coord - Y), lights);
|
|
}
|
|
if (isOpen(coord + X, block, variant)) {
|
|
face(coord, -Z, Y, X, texfaces[1], pickLight(coord + X), lights);
|
|
}
|
|
if (isOpen(coord - X, block, variant)) {
|
|
face(coord, Z, Y, -X, texfaces[0], pickLight(coord - X), lights);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool BlocksRenderer::isOpenForLight(int x, int y, int z) const {
|
|
blockid_t id = voxelsBuffer->pickBlockId(chunk->x * CHUNK_W + x,
|
|
y,
|
|
chunk->z * CHUNK_D + z);
|
|
if (id == BLOCK_VOID) {
|
|
return false;
|
|
}
|
|
const Block& block = *blockDefsCache[id];
|
|
if (block.lightPassing) {
|
|
return true;
|
|
}
|
|
return !id;
|
|
}
|
|
|
|
glm::vec4 BlocksRenderer::pickLight(int x, int y, int z) const {
|
|
if (isOpenForLight(x, y, z)) {
|
|
light_t light = voxelsBuffer->pickLight(chunk->x * CHUNK_W + x, y,
|
|
chunk->z * CHUNK_D + z);
|
|
return glm::vec4(Lightmap::extract(light, 0),
|
|
Lightmap::extract(light, 1),
|
|
Lightmap::extract(light, 2),
|
|
Lightmap::extract(light, 3)) / 15.0f;
|
|
} else {
|
|
return glm::vec4(0.0f);
|
|
}
|
|
}
|
|
|
|
glm::vec4 BlocksRenderer::pickLight(const glm::ivec3& coord) const {
|
|
return pickLight(coord.x, coord.y, coord.z);
|
|
}
|
|
|
|
glm::vec4 BlocksRenderer::pickSoftLight(
|
|
const glm::ivec3& coord, const glm::ivec3& right, const glm::ivec3& up
|
|
) const {
|
|
return (pickLight(coord) +
|
|
pickLight(coord - right) +
|
|
pickLight(coord - right - up) +
|
|
pickLight(coord - up)) * 0.25f;
|
|
}
|
|
|
|
glm::vec4 BlocksRenderer::pickSoftLight(
|
|
float x, float y, float z, const glm::ivec3& right, const glm::ivec3& up
|
|
) const {
|
|
return pickSoftLight({
|
|
static_cast<int>(std::round(x)),
|
|
static_cast<int>(std::round(y)),
|
|
static_cast<int>(std::round(z))},
|
|
right, up);
|
|
}
|
|
|
|
void BlocksRenderer::render(
|
|
const voxel* voxels, const int beginEnds[256][2]
|
|
) {
|
|
bool denseRender = this->denseRender;
|
|
bool densePass = this->densePass;
|
|
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];
|
|
uint8_t variantId = def.getVariantIndex(state.userbits);
|
|
const auto& variant = def.getVariant(variantId);
|
|
if (id == 0 || variant.drawGroup != drawGroup || state.segment) {
|
|
continue;
|
|
}
|
|
if (denseRender != (variant.culling == CullingMode::OPTIONAL)) {
|
|
continue;
|
|
}
|
|
if (def.translucent) {
|
|
continue;
|
|
}
|
|
const UVRegion texfaces[6] {
|
|
cache.getRegion(id, variantId, 0, densePass),
|
|
cache.getRegion(id, variantId, 1, densePass),
|
|
cache.getRegion(id, variantId, 2, densePass),
|
|
cache.getRegion(id, variantId, 3, densePass),
|
|
cache.getRegion(id, variantId, 4, densePass),
|
|
cache.getRegion(id, variantId, 5, densePass)
|
|
};
|
|
int x = i % CHUNK_W;
|
|
int y = i / (CHUNK_D * CHUNK_W);
|
|
int z = (i / CHUNK_D) % CHUNK_W;
|
|
switch (def.getModel(state.userbits).type) {
|
|
case BlockModelType::BLOCK:
|
|
blockCube({x, y, z}, texfaces, def, vox.state, !def.shadeless,
|
|
def.ambientOcclusion);
|
|
break;
|
|
case BlockModelType::XSPRITE: {
|
|
if (!denseRender)
|
|
blockXSprite(x, y, z, glm::vec3(1.0f),
|
|
texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f);
|
|
break;
|
|
}
|
|
case BlockModelType::AABB: {
|
|
if (!denseRender)
|
|
blockAABB({x, y, z}, texfaces, &def, vox.state.rotation,
|
|
!def.shadeless, def.ambientOcclusion);
|
|
break;
|
|
}
|
|
case BlockModelType::CUSTOM: {
|
|
if (!denseRender)
|
|
blockCustomModel(
|
|
{x, y, z},
|
|
def,
|
|
vox.state,
|
|
!def.shadeless,
|
|
def.ambientOcclusion
|
|
);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
if (overflow) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SortingMeshData BlocksRenderer::renderTranslucent(
|
|
const voxel* voxels, int beginEnds[256][2]
|
|
) {
|
|
SortingMeshData sortingMesh {{}};
|
|
|
|
AABB aabb {};
|
|
bool aabbInit = false;
|
|
size_t totalSize = 0;
|
|
|
|
bool densePass = this->densePass;
|
|
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];
|
|
uint8_t variantId = def.getVariantIndex(state.userbits);
|
|
const auto& variant = def.getVariant(variantId);
|
|
if (id == 0 || variant.drawGroup != drawGroup || state.segment) {
|
|
continue;
|
|
}
|
|
if (!def.translucent) {
|
|
continue;
|
|
}
|
|
const UVRegion texfaces[6] {
|
|
cache.getRegion(id, variantId, 0, densePass),
|
|
cache.getRegion(id, variantId, 1, densePass),
|
|
cache.getRegion(id, variantId, 2, densePass),
|
|
cache.getRegion(id, variantId, 3, densePass),
|
|
cache.getRegion(id, variantId, 4, densePass),
|
|
cache.getRegion(id, variantId, 5, densePass)
|
|
};
|
|
int x = i % CHUNK_W;
|
|
int y = i / (CHUNK_D * CHUNK_W);
|
|
int z = (i / CHUNK_D) % CHUNK_W;
|
|
switch (def.getModel(state.userbits).type) {
|
|
case BlockModelType::BLOCK:
|
|
blockCube({x, y, z}, texfaces, def, vox.state, !def.shadeless,
|
|
def.ambientOcclusion);
|
|
break;
|
|
case BlockModelType::XSPRITE: {
|
|
blockXSprite(x, y, z, glm::vec3(1.0f),
|
|
texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f);
|
|
break;
|
|
}
|
|
case BlockModelType::AABB: {
|
|
blockAABB({x, y, z}, texfaces, &def, vox.state.rotation,
|
|
!def.shadeless, def.ambientOcclusion);
|
|
break;
|
|
}
|
|
case BlockModelType::CUSTOM: {
|
|
blockCustomModel({x, y, z}, def, vox.state,
|
|
!def.shadeless, def.ambientOcclusion);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
if (vertexCount == 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<ChunkVertex>(indexCount), 0};
|
|
|
|
totalSize += entry.vertexData.size();
|
|
|
|
for (int j = 0; j < indexCount; j++) {
|
|
std::memcpy(
|
|
entry.vertexData.data() + j,
|
|
vertexBuffer.get() + indexBuffer[j],
|
|
sizeof(ChunkVertex)
|
|
);
|
|
ChunkVertex& vertex = entry.vertexData[j];
|
|
|
|
if (!aabbInit) {
|
|
aabbInit = true;
|
|
aabb.a = aabb.b = vertex.position;
|
|
} else {
|
|
aabb.addPoint(vertex.position);
|
|
}
|
|
|
|
vertex.position.x += chunk->x * CHUNK_W + 0.5f;
|
|
vertex.position.y += 0.5f;
|
|
vertex.position.z += chunk->z * CHUNK_D + 0.5f;
|
|
}
|
|
sortingMesh.entries.push_back(std::move(entry));
|
|
vertexCount = 0;
|
|
vertexOffset = indexCount = 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<ChunkVertex>(totalSize),
|
|
0
|
|
};
|
|
size_t offset = 0;
|
|
for (const auto& entry : sortingMesh.entries) {
|
|
std::memcpy(
|
|
newEntry.vertexData.data() + offset,
|
|
entry.vertexData.data(),
|
|
entry.vertexData.size() * sizeof(ChunkVertex)
|
|
);
|
|
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, settings.graphics.backlight.get());
|
|
|
|
if (voxelsBuffer->pickBlockId(
|
|
chunk->x * CHUNK_W, 0, chunk->z * CHUNK_D
|
|
) == BLOCK_VOID) {
|
|
cancelled = true;
|
|
return;
|
|
}
|
|
const voxel* voxels = chunk->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];
|
|
const auto& variant = def.getVariantByBits(vox.state.userbits);
|
|
|
|
if (beginEnds[variant.drawGroup][0] == 0) {
|
|
beginEnds[variant.drawGroup][0] = i+1;
|
|
}
|
|
beginEnds[variant.drawGroup][1] = i;
|
|
}
|
|
cancelled = false;
|
|
|
|
overflow = false;
|
|
vertexCount = 0;
|
|
vertexOffset = indexCount = 0;
|
|
|
|
denseRender = false;
|
|
densePass = false;
|
|
|
|
sortingMesh = renderTranslucent(voxels, beginEnds);
|
|
|
|
overflow = false;
|
|
vertexCount = 0;
|
|
vertexOffset = 0;
|
|
indexCount = 0;
|
|
denseIndexCount = 0;
|
|
|
|
denseRender = false; //settings.graphics.denseRender.get();
|
|
densePass = false;
|
|
render(voxels, beginEnds);
|
|
|
|
size_t endIndex = indexCount;
|
|
|
|
denseRender = true;
|
|
densePass = true;
|
|
render(voxels, beginEnds);
|
|
|
|
denseIndexCount = indexCount;
|
|
for (size_t i = 0; i < denseIndexCount; i++) {
|
|
denseIndexBuffer[i] = indexBuffer[i];
|
|
}
|
|
|
|
indexCount = endIndex;
|
|
densePass = false;
|
|
render(voxels, beginEnds);
|
|
}
|
|
|
|
ChunkMeshData BlocksRenderer::createMesh() {
|
|
return ChunkMeshData {
|
|
MeshData(
|
|
util::Buffer(vertexBuffer.get(), vertexCount),
|
|
std::vector<util::Buffer<uint32_t>> {
|
|
util::Buffer(indexBuffer.get(), indexCount),
|
|
util::Buffer(denseIndexBuffer.get(), denseIndexCount),
|
|
},
|
|
util::Buffer(
|
|
ChunkVertex::ATTRIBUTES, sizeof(ChunkVertex::ATTRIBUTES) / sizeof(VertexAttribute)
|
|
)
|
|
),
|
|
std::move(sortingMesh)
|
|
};
|
|
}
|
|
|
|
ChunkMesh BlocksRenderer::render(const Chunk *chunk, const Chunks *chunks) {
|
|
build(chunk, chunks);
|
|
|
|
return ChunkMesh{std::make_unique<Mesh<ChunkVertex>>(
|
|
vertexBuffer.get(), vertexCount,
|
|
std::vector<IndexBufferData> {
|
|
IndexBufferData {indexBuffer.get(), indexCount},
|
|
IndexBufferData {denseIndexBuffer.get(), denseIndexCount},
|
|
}
|
|
), std::move(sortingMesh)};
|
|
}
|
|
|
|
VoxelsVolume* BlocksRenderer::getVoxelsBuffer() const {
|
|
return voxelsBuffer.get();
|
|
}
|
|
|
|
size_t BlocksRenderer::getMemoryConsumption() const {
|
|
size_t volume = voxelsBuffer->getW() * voxelsBuffer->getH() * voxelsBuffer->getD();
|
|
return capacity * (sizeof(ChunkVertex) + sizeof(uint32_t) * 2) + volume * (sizeof(voxel) + sizeof(light_t));
|
|
}
|