Refactor mesh handling to use templated vertex structures for improved type safety and flexibility

This commit is contained in:
REDxEYE
2025-04-13 17:14:28 +03:00
parent bba647db5c
commit 1ffbeb8148
28 changed files with 591 additions and 521 deletions
+92 -98
View File
@@ -11,24 +11,25 @@
const glm::vec3 BlocksRenderer::SUN_VECTOR (0.2275f,0.9388f,-0.1005f);
BlocksRenderer::BlocksRenderer(
size_t capacity,
const Content& content,
const ContentGfxCache& cache,
const EngineSettings& settings
) : content(content),
vertexBuffer(std::make_unique<float[]>(capacity * CHUNK_VERTEX_SIZE)),
indexBuffer(std::make_unique<int[]>(capacity)),
vertexBuffer(std::make_unique<ChunkVertex[]>(capacity)),
indexBuffer(std::make_unique<uint32_t[]>(capacity)),
vertexCount(0),
vertexOffset(0),
indexOffset(0),
indexSize(0),
indexCount(0),
capacity(capacity),
cache(cache),
settings(settings)
settings(settings)
{
voxelsBuffer = std::make_unique<VoxelsVolume>(
CHUNK_W + voxelBufferPadding*2,
CHUNK_H,
CHUNK_W + voxelBufferPadding*2,
CHUNK_H,
CHUNK_D + voxelBufferPadding*2);
blockDefsCache = content.getIndices()->blocks.getDefs();
}
@@ -40,39 +41,31 @@ BlocksRenderer::~BlocksRenderer() {
void BlocksRenderer::vertex(
const glm::vec3& coord, float u, float v, const glm::vec4& light
) {
vertexBuffer[vertexOffset++] = coord.x;
vertexBuffer[vertexOffset++] = coord.y;
vertexBuffer[vertexOffset++] = coord.z;
vertexBuffer[vertexOffset++] = u;
vertexBuffer[vertexOffset++] = v;
vertexBuffer[vertexCount].position = coord;
union {
float floating;
uint32_t integer;
} compressed;
vertexBuffer[vertexCount].uv = {u,v};
compressed.integer = (static_cast<uint32_t>(light.r * 255) & 0xff) << 24;
compressed.integer |= (static_cast<uint32_t>(light.g * 255) & 0xff) << 16;
compressed.integer |= (static_cast<uint32_t>(light.b * 255) & 0xff) << 8;
compressed.integer |= (static_cast<uint32_t>(light.a * 255) & 0xff);
vertexBuffer[vertexOffset++] = compressed.floating;
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(int a, int b, int c, int d, int e, int f) {
indexBuffer[indexSize++] = indexOffset + a;
indexBuffer[indexSize++] = indexOffset + b;
indexBuffer[indexSize++] = indexOffset + c;
indexBuffer[indexSize++] = indexOffset + d;
indexBuffer[indexSize++] = indexOffset + e;
indexBuffer[indexSize++] = indexOffset + f;
indexOffset += 4;
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,
const glm::vec3& coord,
float w, float h, float d,
const glm::vec3& axisX,
const glm::vec3& axisY,
@@ -81,7 +74,7 @@ void BlocksRenderer::face(
const glm::vec4(&lights)[4],
const glm::vec4& tint
) {
if (vertexOffset + CHUNK_VERTEX_SIZE * 4 > capacity) {
if (vertexCount + 4 >= capacity) {
overflow = true;
return;
}
@@ -97,7 +90,7 @@ void BlocksRenderer::face(
}
void BlocksRenderer::vertexAO(
const glm::vec3& coord,
const glm::vec3& coord,
float u, float v,
const glm::vec4& tint,
const glm::vec3& axisX,
@@ -121,7 +114,7 @@ void BlocksRenderer::faceAO(
const UVRegion& region,
bool lights
) {
if (vertexOffset + CHUNK_VERTEX_SIZE * 4 > capacity) {
if (vertexCount + 4 >= capacity) {
overflow = true;
return;
}
@@ -159,7 +152,7 @@ void BlocksRenderer::face(
glm::vec4 tint,
bool lights
) {
if (vertexOffset + CHUNK_VERTEX_SIZE * 4 > capacity) {
if (vertexCount + 4 >= capacity) {
overflow = true;
return;
}
@@ -178,10 +171,10 @@ void BlocksRenderer::face(
}
void BlocksRenderer::blockXSprite(
int x, int y, int z,
const glm::vec3& size,
const UVRegion& texface1,
const UVRegion& texface2,
int x, int y, int z,
const glm::vec3& size,
const UVRegion& texface1,
const UVRegion& texface2,
float spread
) {
glm::vec4 lights1[] {
@@ -207,12 +200,12 @@ void BlocksRenderer::blockXSprite(
face({x + xs, y, z + zs}, w, size.y, 0, {-1, 0, 1}, {0, 1, 0}, glm::vec3(),
texface1, lights2, tint);
face({x + xs, y, z + zs}, w, size.y, 0, {1, 0, 1}, {0, 1, 0}, glm::vec3(),
face({x + xs, y, z + zs}, w, size.y, 0, {1, 0, 1}, {0, 1, 0}, glm::vec3(),
texface1, lights1, tint);
face({x + xs, y, z + zs}, w, size.y, 0, {-1, 0, -1}, {0, 1, 0}, glm::vec3(),
face({x + xs, y, z + zs}, w, size.y, 0, {-1, 0, -1}, {0, 1, 0}, glm::vec3(),
texface2, lights2, tint);
face({x + xs, y, z + zs}, w, size.y, 0, {1, 0, -1}, {0, 1, 0}, glm::vec3(),
face({x + xs, y, z + zs}, w, size.y, 0, {1, 0, -1}, {0, 1, 0}, glm::vec3(),
texface2, lights1, tint);
}
@@ -221,8 +214,8 @@ void BlocksRenderer::blockXSprite(
/// @brief AABB blocks render method
void BlocksRenderer::blockAABB(
const glm::ivec3& icoord,
const UVRegion(&texfaces)[6],
const Block* block,
const UVRegion(&texfaces)[6],
const Block* block,
ubyte rotation,
bool lights,
bool ao
@@ -249,7 +242,7 @@ void BlocksRenderer::blockAABB(
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
@@ -289,7 +282,7 @@ void BlocksRenderer::blockCustomModel(
const auto& model = cache.getModel(block->rt.id);
for (const auto& mesh : model.meshes) {
if (vertexOffset + CHUNK_VERTEX_SIZE * mesh.vertices.size() > capacity) {
if (vertexCount + mesh.vertices.size() >= capacity) {
overflow = true;
return;
}
@@ -314,7 +307,7 @@ void BlocksRenderer::blockCustomModel(
r,
n
);
indexBuffer[indexSize++] = indexOffset++;
indexBuffer[indexCount++] = vertexOffset++;
}
}
}
@@ -322,9 +315,9 @@ void BlocksRenderer::blockCustomModel(
/* Fastest solid shaded blocks render method */
void BlocksRenderer::blockCube(
const glm::ivec3& coord,
const UVRegion(&texfaces)[6],
const Block& block,
const glm::ivec3& coord,
const UVRegion(&texfaces)[6],
const Block& block,
blockstate states,
bool lights,
bool ao
@@ -340,7 +333,7 @@ void BlocksRenderer::blockCube(
Y = orient.axes[1];
Z = orient.axes[2];
}
if (ao) {
if (isOpen(coord + Z, block)) {
faceAO(coord, X, Y, Z, texfaces[5], lights);
@@ -383,8 +376,8 @@ void BlocksRenderer::blockCube(
}
bool BlocksRenderer::isOpenForLight(int x, int y, int z) const {
blockid_t id = voxelsBuffer->pickBlockId(chunk->x * CHUNK_W + x,
y,
blockid_t id = voxelsBuffer->pickBlockId(chunk->x * CHUNK_W + x,
y,
chunk->z * CHUNK_D + z);
if (id == BLOCK_VOID) {
return false;
@@ -398,7 +391,7 @@ bool BlocksRenderer::isOpenForLight(int x, int y, int z) const {
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,
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),
@@ -426,8 +419,8 @@ 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(x)),
static_cast<int>(std::round(y)),
static_cast<int>(std::round(z))},
right, up);
}
@@ -466,17 +459,17 @@ void BlocksRenderer::render(
def.ambientOcclusion);
break;
case BlockModel::xsprite: {
blockXSprite(x, y, z, glm::vec3(1.0f),
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,
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,
blockCustomModel({x, y, z}, &def, vox.state.rotation,
!def.shadeless, def.ambientOcclusion);
break;
}
@@ -529,24 +522,24 @@ SortingMeshData BlocksRenderer::renderTranslucent(
def.ambientOcclusion);
break;
case BlockModel::xsprite: {
blockXSprite(x, y, z, glm::vec3(1.0f),
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,
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,
blockCustomModel({x, y, z}, &def, vox.state.rotation,
!def.shadeless, def.ambientOcclusion);
break;
}
default:
break;
}
if (vertexOffset == 0) {
if (vertexCount == 0) {
continue;
}
SortingMeshEntry entry {
@@ -555,50 +548,50 @@ SortingMeshData BlocksRenderer::renderTranslucent(
y + 0.5f,
z + chunk->z * CHUNK_D + 0.5f
),
util::Buffer<float>(indexSize * CHUNK_VERTEX_SIZE), 0};
util::Buffer<ChunkVertex>(indexCount), 0};
totalSize += entry.vertexData.size();
for (int j = 0; j < indexSize; j++) {
for (int j = 0; j < indexCount; j++) {
std::memcpy(
entry.vertexData.data() + j * CHUNK_VERTEX_SIZE,
vertexBuffer.get() + indexBuffer[j] * CHUNK_VERTEX_SIZE,
sizeof(float) * CHUNK_VERTEX_SIZE
entry.vertexData.data() + j,
vertexBuffer.get() + indexBuffer[j],
sizeof(ChunkVertex)
);
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];
ChunkVertex& vertex = entry.vertexData[j];
if (!aabbInit) {
aabbInit = true;
aabb.a = aabb.b = {vx, vy, vz};
aabb.a = aabb.b = vertex.position;
} else {
aabb.addPoint(glm::vec3(vx, vy, vz));
aabb.addPoint(vertex.position);
}
vx += chunk->x * CHUNK_W + 0.5f;
vy += 0.5f;
vz += chunk->z * CHUNK_D + 0.5f;
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));
vertexOffset = 0;
indexOffset = indexSize = 0;
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) &&
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<float>(totalSize),
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(float)
entry.vertexData.data(),
entry.vertexData.size() * sizeof(ChunkVertex)
);
offset += entry.vertexData.size();
}
@@ -630,7 +623,7 @@ void BlocksRenderer::build(const Chunk* chunk, const Chunks* chunks) {
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;
}
@@ -639,36 +632,37 @@ void BlocksRenderer::build(const Chunk* chunk, const Chunks* chunks) {
cancelled = false;
overflow = false;
vertexOffset = 0;
indexOffset = indexSize = 0;
vertexCount = 0;
vertexOffset = indexCount = 0;
sortingMesh = renderTranslucent(voxels, beginEnds);
overflow = false;
vertexCount = 0;
vertexOffset = 0;
indexOffset = indexSize = 0;
indexCount = 0;
render(voxels, beginEnds);
}
ChunkMeshData BlocksRenderer::createMesh() {
return ChunkMeshData {
return ChunkMeshData{
MeshData(
util::Buffer<float>(vertexBuffer.get(), vertexOffset),
util::Buffer<int>(indexBuffer.get(), indexSize),
util::Buffer<VertexAttribute>(
CHUNK_VATTRS, sizeof(CHUNK_VATTRS) / sizeof(VertexAttribute)
util::Buffer(vertexBuffer.get(), vertexCount),
util::Buffer(indexBuffer.get(), indexCount),
util::Buffer(
ChunkVertex::ATTRIBUTES, sizeof(ChunkVertex::ATTRIBUTES) / sizeof(VertexAttribute)
)
),
std::move(sortingMesh)};
std::move(sortingMesh)
};
}
ChunkMesh BlocksRenderer::render(const Chunk* chunk, const Chunks* chunks) {
ChunkMesh BlocksRenderer::render(const Chunk *chunk, const Chunks *chunks) {
build(chunk, chunks);
size_t vcount = vertexOffset / CHUNK_VERTEX_SIZE;
return ChunkMesh{std::make_unique<Mesh>(
vertexBuffer.get(), vcount, indexBuffer.get(), indexSize, CHUNK_VATTRS
return ChunkMesh{std::make_unique<Mesh<ChunkVertex>>(
vertexBuffer.get(), vertexCount, indexBuffer.get(), indexCount, ChunkVertex::ATTRIBUTES
), std::move(sortingMesh)};
}