121 lines
4.8 KiB
C++
121 lines
4.8 KiB
C++
#include "GreedyMesher.h"
|
|
#include <algorithm>
|
|
|
|
// Helper to index into voxels with bounds checking
|
|
inline int voxelAt(const std::vector<std::vector<std::vector<int>>>& v, int x, int y, int z) {
|
|
if (x<0||y<0||z<0) return 0;
|
|
if (x>= (int)v.size() || y>= (int)v[0].size() || z>= (int)v[0][0].size()) return 0;
|
|
return v[x][y][z];
|
|
}
|
|
|
|
std::vector<Quad> GreedyMesher::mesh(const std::vector<std::vector<std::vector<int>>>& voxels) {
|
|
std::vector<Quad> quads;
|
|
int sizeX = voxels.size();
|
|
int sizeY = voxels[0].size();
|
|
int sizeZ = voxels[0][0].size();
|
|
|
|
// directions: {dx,dy,dz}, and their “du” and “dv” axes
|
|
static const int dirs[6][3] = {
|
|
{-1,0,0},{1,0,0},{0,-1,0},{0,1,0},{0,0,-1},{0,0,1}
|
|
};
|
|
static const int axisU[6] = {2,2,0,0,0,0}; // u-axis index per face
|
|
static const int axisV[6] = {1,1,1,1,2,2}; // v-axis index per face
|
|
|
|
// For each face direction
|
|
for (int d = 0; d < 6; d++) {
|
|
int dx = dirs[d][0], dy = dirs[d][1], dz = dirs[d][2];
|
|
int u = axisU[d], v = axisV[d];
|
|
|
|
// dims: dimensions along u,v, and w (the face-normal axis)
|
|
int dimU = (u==0?sizeX:(u==1?sizeY:sizeZ));
|
|
int dimV = (v==0?sizeX:(v==1?sizeY:sizeZ));
|
|
int dimW = (dx!=0?sizeX:(dy!=0?sizeY:sizeZ));
|
|
|
|
// Allocate mask[dimU][dimV]
|
|
std::vector<int> mask(dimU * dimV, 0);
|
|
|
|
// Sweep along the w axis
|
|
for (int w = 0; w <= dimW; w++) {
|
|
// build mask
|
|
for (int i = 0; i < dimU; i++) {
|
|
for (int j = 0; j < dimV; j++) {
|
|
int x,y,z;
|
|
// map (i,j,w) to (x,y,z)
|
|
int coord[3];
|
|
coord[u] = i;
|
|
coord[v] = j;
|
|
coord[ (dx!=0?0:(dy!=0?1:2)) ] = w;
|
|
x = coord[0]; y = coord[1]; z = coord[2];
|
|
int a = voxelAt(voxels, x, y, z);
|
|
coord[ (dx!=0?0:(dy!=0?1:2)) ] = w-1;
|
|
int b = voxelAt(voxels, coord[0], coord[1], coord[2]);
|
|
// if face between a and b should be drawn
|
|
if ((a!=0) != (b!=0)) {
|
|
// a is solid, b is empty → draw face on b side
|
|
mask[i + j*dimU] = (a!=0 ? a : -b);
|
|
} else {
|
|
mask[i + j*dimU] = 0;
|
|
}
|
|
}
|
|
}
|
|
// Greedy merge mask
|
|
for (int j = 0; j < dimV; j++) {
|
|
for (int i = 0; i < dimU; ) {
|
|
int c = mask[i + j*dimU];
|
|
if (c != 0) {
|
|
// determine width
|
|
int wdt = 1;
|
|
while (i+wdt < dimU && mask[i+wdt + j*dimU] == c) wdt++;
|
|
// determine height
|
|
int hgt = 1;
|
|
bool done = false;
|
|
while (j+hgt < dimV && !done) {
|
|
for (int k = 0; k < wdt; k++) {
|
|
if (mask[i+k + (j+hgt)*dimU] != c) {
|
|
done = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!done) hgt++;
|
|
}
|
|
// emit quad
|
|
Quad q;
|
|
// base position
|
|
float pos[3] = {0,0,0};
|
|
pos[0] = pos[1] = pos[2] = 0;
|
|
// set w axis coordinate
|
|
pos[(dx!=0?0:(dy!=0?1:2))] = w;
|
|
// but if this is a “back” face (a was empty, b solid), shift pos back
|
|
if (c < 0) pos[(dx!=0?0:(dy!=0?1:2))] = w-1;
|
|
// then set u,v
|
|
pos[u] = i;
|
|
pos[v] = j;
|
|
q.x = pos[0];
|
|
q.y = pos[1];
|
|
q.z = pos[2];
|
|
// set du and dv vectors
|
|
q.du[0] = q.du[1] = q.du[2] = 0;
|
|
q.dv[0] = q.dv[1] = q.dv[2] = 0;
|
|
q.du[u] = wdt;
|
|
q.dv[v] = hgt;
|
|
// normal
|
|
q.normal = d;
|
|
// textureID
|
|
q.textureID = abs(c);
|
|
quads.push_back(q);
|
|
// zero out mask
|
|
for (int jj = 0; jj < hgt; jj++)
|
|
for (int ii = 0; ii < wdt; ii++)
|
|
mask[i+ii + (j+jj)*dimU] = 0;
|
|
// advance
|
|
i += wdt;
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return quads;
|
|
}
|