Refactor mesh handling to use templated vertex structures for improved type safety and flexibility
This commit is contained in:
@@ -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)};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user