2022-11-10 19:47:39 +01:00
|
|
|
#include <array>
|
2022-11-20 19:14:11 +01:00
|
|
|
#include <memory>
|
2022-11-10 19:47:39 +01:00
|
|
|
|
|
|
|
#include "block.hpp"
|
|
|
|
#include "chunk.hpp"
|
2023-02-12 14:36:59 +01:00
|
|
|
#include "chunkmesher.hpp"
|
2022-11-10 19:47:39 +01:00
|
|
|
#include "globals.hpp"
|
|
|
|
#include "spacefilling.hpp"
|
|
|
|
#include "utils.hpp"
|
|
|
|
|
2023-03-03 21:33:11 +01:00
|
|
|
namespace chunkmesher{
|
|
|
|
|
2023-02-12 14:36:59 +01:00
|
|
|
void mesh(Chunk::Chunk* chunk)
|
2022-11-10 19:47:39 +01:00
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Taking inspiration from 0fps and the jme3 porting at
|
|
|
|
* https://github.com/roboleary/GreedyMesh/blob/master/src/mygame/Main.java
|
|
|
|
*
|
|
|
|
* By carefully re-reading the code and the blog post, I've come to the
|
|
|
|
* realization that I wrote something very similar yet a lot messier (and
|
|
|
|
* uglier) back in
|
|
|
|
* https://github.com/EmaMaker/voxel-engine-jme3/blob/master/src/voxelengine
|
|
|
|
* /world/Chunk.java
|
|
|
|
*
|
|
|
|
* Reading roboleary's impl. I've learned how to optimize having to loop
|
|
|
|
* across different planes everytime I change dimension without having to
|
|
|
|
* write 3 separate 3-nested-for-loops
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* It's not feasible to just create a new mesh everytime a quad needs placing.
|
|
|
|
* This ends up creating TONS of meshes and the game will just lag out.
|
|
|
|
* As I did in the past, it's better to create a single mesh for each chunk,
|
|
|
|
* containing all the quads. In the future, maybe translucent blocks and liquids
|
|
|
|
* will need a separate mesh, but still on a per-chunk basis
|
|
|
|
*/
|
2023-03-03 21:33:11 +01:00
|
|
|
chunk->vertices.clear();
|
|
|
|
chunk->indices.clear();
|
|
|
|
chunk->vIndex = 0;
|
2022-11-10 19:47:39 +01:00
|
|
|
|
2023-03-03 21:33:11 +01:00
|
|
|
if(chunk->getState(Chunk::CHUNK_STATE_EMPTY)) return;
|
2023-02-12 14:36:59 +01:00
|
|
|
|
2022-11-10 19:47:39 +01:00
|
|
|
// convert tree to array since it is easier to work with it
|
2022-11-20 19:14:11 +01:00
|
|
|
int length{0};
|
2023-02-12 14:36:59 +01:00
|
|
|
Block *blocks = chunk->getBlocksArray(&length);
|
2023-02-22 21:54:15 +01:00
|
|
|
if(length == 0) return;
|
|
|
|
|
2022-11-10 19:47:39 +01:00
|
|
|
int k, l, u, v, w, h, n, j, i;
|
|
|
|
int x[]{0, 0, 0};
|
|
|
|
int q[]{0, 0, 0};
|
|
|
|
int du[]{0, 0, 0};
|
|
|
|
int dv[]{0, 0, 0};
|
|
|
|
|
|
|
|
std::array<Block, CHUNK_SIZE * CHUNK_SIZE> mask;
|
|
|
|
for (bool backFace = true, b = false; b != backFace; backFace = backFace && b, b = !b)
|
|
|
|
{
|
|
|
|
// iterate over 3 dimensions
|
|
|
|
for (int dim = 0; dim < 3; dim++)
|
|
|
|
{
|
|
|
|
// offsets of other 2 axes
|
|
|
|
u = (dim + 1) % 3;
|
|
|
|
v = (dim + 2) % 3;
|
|
|
|
|
|
|
|
x[0] = 0;
|
|
|
|
x[1] = 0;
|
|
|
|
x[2] = 0;
|
|
|
|
|
|
|
|
q[0] = 0;
|
|
|
|
q[1] = 0;
|
|
|
|
q[2] = 0;
|
|
|
|
q[dim] = 1; // easily mark which dimension we are comparing
|
|
|
|
// voxels
|
|
|
|
// on
|
|
|
|
|
|
|
|
for (x[dim] = -1; x[dim] < CHUNK_SIZE;)
|
|
|
|
{
|
|
|
|
n = 0;
|
|
|
|
|
|
|
|
for (x[v] = 0; x[v] < CHUNK_SIZE; x[v]++)
|
|
|
|
{
|
|
|
|
for (x[u] = 0; x[u] < CHUNK_SIZE; x[u]++)
|
|
|
|
{
|
2022-12-01 23:37:57 +01:00
|
|
|
Block b1 = (x[dim] >= 0) ? blocks[HILBERT_XYZ_ENCODE[x[0]][x[1]][x[2]]] : Block::NULLBLK;
|
2022-11-10 19:47:39 +01:00
|
|
|
Block b2 = (x[dim] < CHUNK_SIZE - 1)
|
2022-11-20 20:22:56 +01:00
|
|
|
? blocks[HILBERT_XYZ_ENCODE[x[0] + q[0]][x[1] + q[1]][x[2] + q[2]]]
|
2022-12-01 23:37:57 +01:00
|
|
|
: Block::NULLBLK;
|
2022-11-10 19:47:39 +01:00
|
|
|
|
|
|
|
// This is the original line taken from rob's code, readapted (replace voxelFace
|
|
|
|
// with b1 and b2).
|
|
|
|
// mask[n++] = ((voxelFace != Block::NULLBLK && voxelFace1 != Block::NULLBLK &&
|
|
|
|
// voxelFace.equals(voxelFace1))) ? Block::NULLBLK : backFace ? voxelFace1 : voxelFace;
|
|
|
|
|
|
|
|
// Additionally checking whether b1 and b2 are AIR or Block::NULLBLK allows face culling,
|
|
|
|
// thus not rendering faces that cannot be seen
|
2022-11-10 22:46:47 +01:00
|
|
|
// Removing the control for Block::NULLBLK disables chunk borders
|
|
|
|
// This can be surely refactored in something that isn't such a big one-liner
|
|
|
|
mask[n++] = b1 != Block::NULLBLK && b2 != Block::NULLBLK && b1 == b2 ? Block::NULLBLK
|
|
|
|
: backFace ? b1 == Block::AIR || b1 == Block::NULLBLK ? b2 : Block::NULLBLK
|
2022-11-10 23:29:34 +01:00
|
|
|
: b2 == Block::AIR || b2 == Block::NULLBLK ? b1
|
|
|
|
: Block::NULLBLK;
|
2022-11-10 19:47:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
x[dim]++;
|
|
|
|
n = 0;
|
2022-11-20 19:14:11 +01:00
|
|
|
// Actually generate the mesh from the mask. This is the same thing I used in my old crappy voxel engine
|
2022-11-10 19:47:39 +01:00
|
|
|
for (j = 0; j < CHUNK_SIZE; j++)
|
|
|
|
{
|
|
|
|
for (i = 0; i < CHUNK_SIZE;)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (mask[n] != Block::NULLBLK)
|
|
|
|
{
|
|
|
|
// First compute the width
|
|
|
|
for (w = 1; i + w < CHUNK_SIZE && mask[n + w] != Block::NULLBLK && mask[n] == mask[n + w]; w++)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool done = false;
|
|
|
|
for (h = 1; j + h < CHUNK_SIZE; h++)
|
|
|
|
{
|
|
|
|
for (k = 0; k < w; k++)
|
|
|
|
{
|
|
|
|
if (mask[n + k + h * CHUNK_SIZE] == Block::NULLBLK || mask[n + k + h * CHUNK_SIZE] != mask[n])
|
|
|
|
{
|
|
|
|
done = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (done)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mask[n] != Block::AIR)
|
|
|
|
{
|
|
|
|
x[u] = i;
|
|
|
|
x[v] = j;
|
|
|
|
|
|
|
|
du[0] = 0;
|
|
|
|
du[1] = 0;
|
|
|
|
du[2] = 0;
|
|
|
|
du[u] = w;
|
|
|
|
|
|
|
|
dv[0] = 0;
|
|
|
|
dv[1] = 0;
|
|
|
|
dv[2] = 0;
|
|
|
|
dv[v] = h;
|
|
|
|
|
2023-03-03 21:33:11 +01:00
|
|
|
quad(chunk, glm::vec3(x[0], x[1], x[2]),
|
2022-11-10 19:47:39 +01:00
|
|
|
glm::vec3(x[0] + du[0], x[1] + du[1], x[2] + du[2]),
|
|
|
|
glm::vec3(x[0] + du[0] + dv[0], x[1] + du[1] + dv[1],
|
|
|
|
x[2] + du[2] + dv[2]),
|
|
|
|
glm::vec3(x[0] + dv[0], x[1] + dv[1], x[2] + dv[2]),
|
|
|
|
mask[n], backFace);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (l = 0; l < h; ++l)
|
|
|
|
{
|
|
|
|
for (k = 0; k < w; ++k)
|
|
|
|
{
|
|
|
|
mask[n + k + l * CHUNK_SIZE] = Block::NULLBLK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* And then finally increment the counters and
|
|
|
|
* continue
|
|
|
|
*/
|
|
|
|
i += w;
|
|
|
|
n += w;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
i++;
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-22 21:54:15 +01:00
|
|
|
delete[] blocks;
|
2023-03-03 21:33:11 +01:00
|
|
|
|
2022-11-10 19:47:39 +01:00
|
|
|
}
|
|
|
|
|
2023-02-12 14:36:59 +01:00
|
|
|
void draw(Chunk::Chunk* chunk, glm::mat4 model)
|
2022-11-10 19:47:39 +01:00
|
|
|
{
|
|
|
|
|
2022-11-10 23:29:34 +01:00
|
|
|
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // wireframe mode
|
2023-03-03 21:33:11 +01:00
|
|
|
if(chunk->getState(Chunk::CHUNK_STATE_MESH_LOADED))
|
2022-11-20 19:14:11 +01:00
|
|
|
{
|
|
|
|
theShader->use();
|
2023-02-12 14:36:59 +01:00
|
|
|
theShader->setMat4("model", model);
|
2022-11-20 19:14:11 +01:00
|
|
|
theShader->setMat4("view", theCamera.getView());
|
|
|
|
theShader->setMat4("projection", theCamera.getProjection());
|
2022-11-10 19:47:39 +01:00
|
|
|
|
2023-02-12 14:36:59 +01:00
|
|
|
glBindVertexArray(chunk->VAO);
|
|
|
|
glDrawElements(GL_TRIANGLES, chunk->vIndex , GL_UNSIGNED_INT, 0);
|
2022-11-20 19:14:11 +01:00
|
|
|
glBindVertexArray(0);
|
|
|
|
}
|
2022-11-10 19:47:39 +01:00
|
|
|
}
|
|
|
|
|
2023-03-03 21:33:11 +01:00
|
|
|
void quad(Chunk::Chunk* chunk, glm::vec3 bottomLeft, glm::vec3 topLeft, glm::vec3 topRight, glm::vec3 bottomRight, Block block, bool backFace)
|
2022-11-10 19:47:39 +01:00
|
|
|
{
|
2022-11-10 22:46:47 +01:00
|
|
|
|
2023-03-03 21:33:11 +01:00
|
|
|
chunk->vertices.push_back(bottomLeft.x);
|
|
|
|
chunk->vertices.push_back(bottomLeft.y);
|
|
|
|
chunk->vertices.push_back(bottomLeft.z);
|
2022-11-10 19:47:39 +01:00
|
|
|
|
2023-03-03 21:33:11 +01:00
|
|
|
chunk->vertices.push_back(bottomRight.x);
|
|
|
|
chunk->vertices.push_back(bottomRight.y);
|
|
|
|
chunk->vertices.push_back(bottomRight.z);
|
2022-11-20 19:14:11 +01:00
|
|
|
|
2023-03-03 21:33:11 +01:00
|
|
|
chunk->vertices.push_back(topLeft.x);
|
|
|
|
chunk->vertices.push_back(topLeft.y);
|
|
|
|
chunk->vertices.push_back(topLeft.z);
|
2022-11-10 19:47:39 +01:00
|
|
|
|
2023-03-03 21:33:11 +01:00
|
|
|
chunk->vertices.push_back(topRight.x);
|
|
|
|
chunk->vertices.push_back(topRight.y);
|
|
|
|
chunk->vertices.push_back(topRight.z);
|
2022-11-10 19:47:39 +01:00
|
|
|
|
2022-12-01 23:37:57 +01:00
|
|
|
if (backFace)
|
|
|
|
{
|
2023-03-03 21:33:11 +01:00
|
|
|
chunk->indices.push_back(chunk->vIndex + 2);
|
|
|
|
chunk->indices.push_back(chunk->vIndex);
|
|
|
|
chunk->indices.push_back(chunk->vIndex + 1);
|
|
|
|
chunk->indices.push_back(chunk->vIndex + 1);
|
|
|
|
chunk->indices.push_back(chunk->vIndex + 3);
|
|
|
|
chunk->indices.push_back(chunk->vIndex + 2);
|
2022-12-01 23:37:57 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-03-03 21:33:11 +01:00
|
|
|
chunk->indices.push_back(chunk->vIndex + 2);
|
|
|
|
chunk->indices.push_back(chunk->vIndex + 3);
|
|
|
|
chunk->indices.push_back(chunk->vIndex + 1);
|
|
|
|
chunk->indices.push_back(chunk->vIndex + 1);
|
|
|
|
chunk->indices.push_back(chunk->vIndex);
|
|
|
|
chunk->indices.push_back(chunk->vIndex + 2);
|
2022-12-01 23:37:57 +01:00
|
|
|
}
|
2023-03-03 21:33:11 +01:00
|
|
|
chunk->vIndex += 4;
|
2022-11-10 19:47:39 +01:00
|
|
|
|
2022-11-11 12:04:11 +01:00
|
|
|
// ugly switch case
|
2022-11-10 22:46:47 +01:00
|
|
|
GLfloat r, g, b;
|
|
|
|
switch (block)
|
|
|
|
{
|
|
|
|
case Block::STONE:
|
|
|
|
r = 0.588f;
|
|
|
|
g = 0.588f;
|
|
|
|
b = 0.588f;
|
|
|
|
break;
|
|
|
|
case Block::GRASS:
|
|
|
|
r = 0.05f;
|
2022-11-11 12:04:11 +01:00
|
|
|
g = 0.725f;
|
2022-11-10 22:46:47 +01:00
|
|
|
b = 0.0f;
|
|
|
|
break;
|
|
|
|
case Block::DIRT:
|
|
|
|
r = 0.176f;
|
|
|
|
g = 0.282f;
|
|
|
|
b = 0.169f;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
r = 0.0f;
|
|
|
|
g = 0.0f;
|
|
|
|
b = 0.0f;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-11-11 12:04:11 +01:00
|
|
|
// Fake shadows
|
2022-11-10 22:46:47 +01:00
|
|
|
if ((bottomLeft.z == bottomRight.z && bottomRight.z == topLeft.z && topLeft.z == topRight.z))
|
|
|
|
{
|
|
|
|
r += 0.1f;
|
|
|
|
g += 0.1f;
|
|
|
|
b += 0.1f;
|
|
|
|
}
|
|
|
|
|
2022-11-11 12:04:11 +01:00
|
|
|
if ((bottomLeft.y == bottomRight.y && bottomRight.y == topLeft.y && topLeft.y == topRight.y))
|
|
|
|
{
|
|
|
|
r += 0.12f;
|
|
|
|
g += 0.12f;
|
|
|
|
b += 0.12f;
|
|
|
|
}
|
|
|
|
|
2022-11-10 22:46:47 +01:00
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
{
|
2023-03-03 21:33:11 +01:00
|
|
|
chunk->colors.push_back(r);
|
|
|
|
chunk->colors.push_back(g);
|
|
|
|
chunk->colors.push_back(b);
|
2022-11-10 22:46:47 +01:00
|
|
|
}
|
2023-03-03 21:33:11 +01:00
|
|
|
}
|
|
|
|
};
|