feat(core): add chunk mesh vertex and index buffer generation
This commit is contained in:
1 parent
b4723183fd
commit
566f3b6d5a
2 files changed
+232
No files matched your search
@@ -0,0 +1,184 @@
|
||||
#include "vf/Mesh.h"
|
||||
|
||||
namespace vf {
|
||||
|
||||
namespace {
|
||||
|
||||
struct MaskCell {
|
||||
bool set = false;
|
||||
bool backface = false;
|
||||
Block block = Block::Air;
|
||||
int tile = 0;
|
||||
|
||||
bool operator==(const MaskCell& o) const {
|
||||
return set == o.set && backface == o.backface && block == o.block && tile == o.tile;
|
||||
}
|
||||
bool operator!=(const MaskCell& o) const { return !(*this == o); }
|
||||
};
|
||||
|
||||
Face faceFor(int axis, bool backface) {
|
||||
switch (axis) {
|
||||
case 0: return backface ? Face::NegX : Face::PosX;
|
||||
case 1: return backface ? Face::NegY : Face::PosY;
|
||||
default: return backface ? Face::NegZ : Face::PosZ;
|
||||
}
|
||||
}
|
||||
|
||||
void emitQuad(Mesh& mesh, const Vec3& base, const Vec3& du, const Vec3& dv, const Vec3& normal,
|
||||
int width, int height, float tile, bool backface) {
|
||||
Vec3 p0 = base;
|
||||
Vec3 p1 = base + du;
|
||||
Vec3 p2 = base + du + dv;
|
||||
Vec3 p3 = base + dv;
|
||||
|
||||
uint32_t start = static_cast<uint32_t>(mesh.vertices.size());
|
||||
float w = static_cast<float>(width);
|
||||
float h = static_cast<float>(height);
|
||||
|
||||
mesh.vertices.push_back({p0.x, p0.y, p0.z, normal.x, normal.y, normal.z, 0.0f, 0.0f, tile});
|
||||
mesh.vertices.push_back({p1.x, p1.y, p1.z, normal.x, normal.y, normal.z, w, 0.0f, tile});
|
||||
mesh.vertices.push_back({p2.x, p2.y, p2.z, normal.x, normal.y, normal.z, w, h, tile});
|
||||
mesh.vertices.push_back({p3.x, p3.y, p3.z, normal.x, normal.y, normal.z, 0.0f, h, tile});
|
||||
|
||||
if (backface) {
|
||||
mesh.indices.insert(mesh.indices.end(),
|
||||
{start, start + 2, start + 1, start, start + 3, start + 2});
|
||||
} else {
|
||||
mesh.indices.insert(mesh.indices.end(),
|
||||
{start, start + 1, start + 2, start, start + 2, start + 3});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Mesh greedyMeshChunk(const World& world, const Vec3i& chunkCoord) {
|
||||
Mesh mesh;
|
||||
const int origin[3] = {chunkCoord.x * chunkSize, chunkCoord.y * chunkSize,
|
||||
chunkCoord.z * chunkSize};
|
||||
const int dims[3] = {chunkSize, chunkSize, chunkSize};
|
||||
|
||||
auto voxel = [&](int lx, int ly, int lz) {
|
||||
return world.blockAt(origin[0] + lx, origin[1] + ly, origin[2] + lz);
|
||||
};
|
||||
|
||||
std::vector<MaskCell> mask(chunkSize * chunkSize);
|
||||
|
||||
for (int d = 0; d < 3; ++d) {
|
||||
int u = (d + 1) % 3;
|
||||
int v = (d + 2) % 3;
|
||||
int x[3] = {0, 0, 0};
|
||||
int q[3] = {0, 0, 0};
|
||||
q[d] = 1;
|
||||
|
||||
for (x[d] = -1; x[d] < dims[d];) {
|
||||
int n = 0;
|
||||
for (x[v] = 0; x[v] < dims[v]; ++x[v]) {
|
||||
for (x[u] = 0; x[u] < dims[u]; ++x[u], ++n) {
|
||||
Block a = voxel(x[0], x[1], x[2]);
|
||||
Block b = voxel(x[0] + q[0], x[1] + q[1], x[2] + q[2]);
|
||||
|
||||
bool aFace = (x[d] >= 0) && faceVisible(a, b);
|
||||
bool bFace = (x[d] < dims[d] - 1) && faceVisible(b, a);
|
||||
|
||||
MaskCell cell;
|
||||
if (aFace && !bFace) {
|
||||
cell.set = true;
|
||||
cell.backface = false;
|
||||
cell.block = a;
|
||||
cell.tile = faceTile(a, faceFor(d, false));
|
||||
} else if (bFace && !aFace) {
|
||||
cell.set = true;
|
||||
cell.backface = true;
|
||||
cell.block = b;
|
||||
cell.tile = faceTile(b, faceFor(d, true));
|
||||
}
|
||||
mask[n] = cell;
|
||||
}
|
||||
}
|
||||
|
||||
++x[d];
|
||||
|
||||
n = 0;
|
||||
for (int j = 0; j < dims[v]; ++j) {
|
||||
for (int i = 0; i < dims[u];) {
|
||||
MaskCell current = mask[n];
|
||||
if (!current.set) {
|
||||
++i;
|
||||
++n;
|
||||
continue;
|
||||
}
|
||||
|
||||
int width = 1;
|
||||
while (i + width < dims[u] && mask[n + width] == current) ++width;
|
||||
|
||||
int height = 1;
|
||||
bool stop = false;
|
||||
while (j + height < dims[v] && !stop) {
|
||||
for (int k = 0; k < width; ++k) {
|
||||
if (mask[n + k + height * dims[u]] != current) {
|
||||
stop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!stop) ++height;
|
||||
}
|
||||
|
||||
x[u] = i;
|
||||
x[v] = j;
|
||||
|
||||
Vec3 base{static_cast<float>(origin[0] + x[0]),
|
||||
static_cast<float>(origin[1] + x[1]),
|
||||
static_cast<float>(origin[2] + x[2])};
|
||||
Vec3 du{0, 0, 0};
|
||||
Vec3 dv{0, 0, 0};
|
||||
(&du.x)[u] = static_cast<float>(width);
|
||||
(&dv.x)[v] = static_cast<float>(height);
|
||||
|
||||
Vec3 normal{0, 0, 0};
|
||||
(&normal.x)[d] = current.backface ? -1.0f : 1.0f;
|
||||
|
||||
emitQuad(mesh, base, du, dv, normal, width, height,
|
||||
static_cast<float>(current.tile), current.backface);
|
||||
|
||||
for (int l = 0; l < height; ++l)
|
||||
for (int k = 0; k < width; ++k)
|
||||
mask[n + k + l * dims[u]] = MaskCell{};
|
||||
|
||||
i += width;
|
||||
n += width;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
int vertexAo(bool side1, bool side2, bool corner) {
|
||||
if (side1 && side2) return 0;
|
||||
return 3 - (static_cast<int>(side1) + static_cast<int>(side2) + static_cast<int>(corner));
|
||||
}
|
||||
|
||||
int culledFaceCount(const World& world, const Vec3i& chunkCoord) {
|
||||
const int origin[3] = {chunkCoord.x * chunkSize, chunkCoord.y * chunkSize,
|
||||
chunkCoord.z * chunkSize};
|
||||
static const int offsets[6][3] = {{1, 0, 0}, {-1, 0, 0}, {0, 1, 0},
|
||||
{0, -1, 0}, {0, 0, 1}, {0, 0, -1}};
|
||||
int faces = 0;
|
||||
for (int lx = 0; lx < chunkSize; ++lx) {
|
||||
for (int ly = 0; ly < chunkSize; ++ly) {
|
||||
for (int lz = 0; lz < chunkSize; ++lz) {
|
||||
Block self = world.blockAt(origin[0] + lx, origin[1] + ly, origin[2] + lz);
|
||||
if (isAir(self)) continue;
|
||||
for (auto& o : offsets) {
|
||||
Block other = world.blockAt(origin[0] + lx + o[0], origin[1] + ly + o[1],
|
||||
origin[2] + lz + o[2]);
|
||||
if (faceVisible(self, other)) ++faces;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return faces;
|
||||
}
|
||||
|
||||
} // namespace vf
|
||||
Reference in new issue
Block a user