From d19cf166d9bcaa480045d271e5658b900237a309 Mon Sep 17 00:00:00 2001 From: Hulitaphy Date: Sun, 20 Jul 2025 14:01:57 -0400 Subject: [PATCH 1/3] Add transparent mask culling --- src/main.cpp | 5 ++++ src/mesher.h | 64 +++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index a53a28f..15b3579 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -189,6 +189,7 @@ void createTestChunk() { uint8_t* voxels = new uint8_t[CS_P3] { 0 }; memset(voxels, 0, CS_P3); memset(mainThreadMeshData.opaqueMask, 0, CS_P2 * sizeof(uint64_t)); + memset(mainThreadMeshData.transparentMask, 0, CS_P2 * sizeof(uint64_t)); switch (mesh_type) { case (int) MESH_TYPE::TERRAIN: { @@ -285,6 +286,7 @@ void createTestChunk() { auto meshLambda = [](ChunkTableEntry* tableEntry, ThreadData* threadData) -> MeshingResponse { Timer decompressionTimer("", true); memset(threadData->meshData->opaqueMask, 0, CS_P2 * sizeof(uint64_t)); + memset(threadData->meshData->transparentMask, 0, CS_P2 * sizeof(uint64_t)); rle::decompressToVoxelsAndOpaqueMask(levelFile.buffer.data() + tableEntry->rleDataBegin, tableEntry->rleDataSize, threadData->voxels, threadData->meshData->opaqueMask); auto decompressionDurationUs = decompressionTimer.end(); @@ -319,6 +321,7 @@ int main(int argc, char* argv[]) { } mainThreadMeshData.opaqueMask = new uint64_t[CS_P2] { 0 }; + mainThreadMeshData.transparentMask = new uint64_t[CS_P2] { 0 }; mainThreadMeshData.faceMasks = new uint64_t[CS_2 * 6] { 0 }; mainThreadMeshData.forwardMerged = new uint8_t[CS_2] { 0 }; mainThreadMeshData.rightMerged = new uint8_t[CS] { 0 }; @@ -347,6 +350,7 @@ int main(int argc, char* argv[]) { for (int i = 0; i < MAX_MESHING_FUTURES; i++) { auto meshData = new MeshData(); meshData->opaqueMask = new uint64_t[CS_P2] { 0 }; + meshData->transparentMask = new uint64_t[CS_P2] { 0 }; meshData->faceMasks = new uint64_t[CS_2 * 6] { 0 }; meshData->forwardMerged = new uint8_t[CS_2] { 0 }; meshData->rightMerged = new uint8_t[CS] { 0 }; @@ -440,6 +444,7 @@ int main(int argc, char* argv[]) { memset(voxels->data(), 0, CS_P3); memset(mainThreadMeshData.opaqueMask, 0, CS_P2 * sizeof(uint64_t)); + memset(mainThreadMeshData.transparentMask, 0, CS_P2 * sizeof(uint64_t)); noise.generateTerrainV2(voxels->data(), mainThreadMeshData.opaqueMask, x, z, 30); diff --git a/src/mesher.h b/src/mesher.h index ddf6711..7c893ff 100644 --- a/src/mesher.h +++ b/src/mesher.h @@ -55,6 +55,7 @@ static constexpr int CS_P3 = CS_P * CS_P * CS_P; struct MeshData { uint64_t* faceMasks = nullptr; // CS_2 * 6 uint64_t* opaqueMask = nullptr; //CS_P2 + uint64_t* transparentMask = nullptr; //CS_P2 uint8_t* forwardMerged = nullptr; // CS_2 uint8_t* rightMerged = nullptr; // CS BM_VECTOR* vertices = nullptr; @@ -109,27 +110,64 @@ void mesh(const uint8_t* voxels, MeshData& meshData) { int vertexI = 0; uint64_t* opaqueMask = meshData.opaqueMask; + uint64_t* transparentMask = meshData.opaqueMask; uint64_t* faceMasks = meshData.faceMasks; uint8_t* forwardMerged = meshData.forwardMerged; uint8_t* rightMerged = meshData.rightMerged; // Hidden face culling for (int a = 1; a < CS_P - 1; a++) { - const int aCS_P = a * CS_P; + const int a_ = a * CS_P; for (int b = 1; b < CS_P - 1; b++) { - const uint64_t columnBits = opaqueMask[(a * CS_P) + b] & P_MASK; - const int baIndex = (b - 1) + (a - 1) * CS; - const int abIndex = (a - 1) + (b - 1) * CS; - - faceMasks[baIndex + 0 * CS_2] = (columnBits & ~opaqueMask[aCS_P + CS_P + b]) >> 1; - faceMasks[baIndex + 1 * CS_2] = (columnBits & ~opaqueMask[aCS_P - CS_P + b]) >> 1; - - faceMasks[abIndex + 2 * CS_2] = (columnBits & ~opaqueMask[aCS_P + (b + 1)]) >> 1; - faceMasks[abIndex + 3 * CS_2] = (columnBits & ~opaqueMask[aCS_P + (b - 1)]) >> 1; - - faceMasks[baIndex + 4 * CS_2] = columnBits & ~(opaqueMask[aCS_P + b] >> 1); - faceMasks[baIndex + 5 * CS_2] = columnBits & ~(opaqueMask[aCS_P + b] << 1); + const int ab = a_ + b; + const uint64_t opaque_col = opaqueMask[ab] & P_MASK; + const int ba_index = (b - 1) + (a - 1) * CS; + const int ab_index = (a - 1) + (b - 1) * CS; + const int up_faces = ba_index + 0 * CS_2; + const int down_faces = ba_index + 1 * CS_2; + const int right_faces = ab_index + 2 * CS_2; + const int left_faces = ab_index + 3 * CS_2; + const int front_faces = ba_index + 4 * CS_2; + const int back_faces = ba_index + 5 * CS_2; + + // Opaque face culling + faceMasks[up_faces] = (opaque_col & ~opaqueMask[ab + CS_P]) >> 1; + faceMasks[down_faces] = (opaque_col & ~opaqueMask[ab - CS_P]) >> 1; + + faceMasks[right_faces] = (opaque_col & ~opaqueMask[ab + 1]) >> 1; + faceMasks[left_faces] = (opaque_col & ~opaqueMask[ab - 1]) >> 1; + + faceMasks[front_faces] = opaque_col & ~(opaqueMask[ab] >> 1); + faceMasks[back_faces] = opaque_col & ~(opaqueMask[ab] << 1); + + // Transparent face culling + uint64_t bits_here = transparentMask[ab] & P_MASK; + if(bits_here == 0) continue; + + const int ab_ = ab * CS_P; + while (bits_here) + { + unsigned long bitPos; + #ifdef _MSC_VER + _BitScanForward64(&bitPos, bits_here); + #else + bitPos = __builtin_ctzll(bits_here); + #endif + bits_here &= ~(1ull << bitPos); + + if (bitPos == 0) continue; + + const int abc = ab + bitPos; + const uint16_t v1 = voxels[abc]; + // Add faces where the neighboring block is different or air + faceMasks[up_faces] |= (static_cast(v1 != voxels[abc + CS_P2]) << (bitPos - 1)); + faceMasks[down_faces] |= (static_cast(v1 != voxels[abc - CS_P2]) << (bitPos - 1)); + faceMasks[right_faces] |= (static_cast(v1 != voxels[abc + CS_P]) << (bitPos - 1)); + faceMasks[left_faces] |= (static_cast(v1 != voxels[abc - CS_P]) << (bitPos - 1)); + faceMasks[front_faces] |= (static_cast(v1 != voxels[abc + 1]) << bitPos); + faceMasks[back_faces] |= (static_cast(v1 != voxels[abc - 1]) << bitPos); + } } } From af5466521bff5463a0895c34adcc0a3ed47b2ab4 Mon Sep 17 00:00:00 2001 From: Hulitaphy Date: Sun, 20 Jul 2025 14:27:34 -0400 Subject: [PATCH 2/3] Fix bug in transparent meshing, and add transparent support to shaders and an example --- src/main.cpp | 10 ++++++++++ src/mesher.h | 13 ++++++++----- src/misc/noise.h | 28 ++++++++++++++++++++++++++++ src/shaders/main.fs | 9 +++++---- src/shaders/main.vs | 24 ++++++++++++++---------- 5 files changed, 65 insertions(+), 19 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 15b3579..bb0fc97 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -95,6 +95,9 @@ bool init_opengl() { glCullFace(GL_BACK); glEnable(GL_CULL_FACE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glClearColor(0.529f, 0.808f, 0.922f, 0.0f); glEnable(GL_MULTISAMPLE); @@ -116,6 +119,7 @@ float last_y = 0.0f; enum class MESH_TYPE : int { SPHERE, TERRAIN, + TRANSPARENT_TERRAIN, RANDOM, CHECKERBOARD, EMPTY, @@ -197,6 +201,12 @@ void createTestChunk() { break; } + case (int) MESH_TYPE::TRANSPARENT_TERRAIN: { + noise.generateTerrainV1Transparent(voxels, mainThreadMeshData.transparentMask, 30); + std::cout << "YES\n"; + break; + } + case (int) MESH_TYPE::RANDOM: { noise.generateWhiteNoiseTerrain(voxels, mainThreadMeshData.opaqueMask, 30); break; diff --git a/src/mesher.h b/src/mesher.h index 7c893ff..0da3f95 100644 --- a/src/mesher.h +++ b/src/mesher.h @@ -110,7 +110,7 @@ void mesh(const uint8_t* voxels, MeshData& meshData) { int vertexI = 0; uint64_t* opaqueMask = meshData.opaqueMask; - uint64_t* transparentMask = meshData.opaqueMask; + uint64_t* transparentMask = meshData.transparentMask; uint64_t* faceMasks = meshData.faceMasks; uint8_t* forwardMerged = meshData.forwardMerged; uint8_t* rightMerged = meshData.rightMerged; @@ -156,15 +156,18 @@ void mesh(const uint8_t* voxels, MeshData& meshData) { #endif bits_here &= ~(1ull << bitPos); - if (bitPos == 0) continue; + if (bitPos == 0 || bitPos >= CS+1) continue; - const int abc = ab + bitPos; - const uint16_t v1 = voxels[abc]; - // Add faces where the neighboring block is different or air + const int abc = ab_ + bitPos; + const uint8_t v1 = voxels[abc]; + // The transparent step is slower than the opaque step + // because we need to check if neighboring transparent blocks are differents (we don't care about that for opaque blocks) faceMasks[up_faces] |= (static_cast(v1 != voxels[abc + CS_P2]) << (bitPos - 1)); faceMasks[down_faces] |= (static_cast(v1 != voxels[abc - CS_P2]) << (bitPos - 1)); + faceMasks[right_faces] |= (static_cast(v1 != voxels[abc + CS_P]) << (bitPos - 1)); faceMasks[left_faces] |= (static_cast(v1 != voxels[abc - CS_P]) << (bitPos - 1)); + faceMasks[front_faces] |= (static_cast(v1 != voxels[abc + 1]) << bitPos); faceMasks[back_faces] |= (static_cast(v1 != voxels[abc - 1]) << bitPos); } diff --git a/src/misc/noise.h b/src/misc/noise.h index 4fc507c..20adff8 100644 --- a/src/misc/noise.h +++ b/src/misc/noise.h @@ -48,6 +48,34 @@ class Noise { } } + void generateTerrainV1Transparent(uint8_t* voxels, uint64_t* transparentMask, int seed) { + noise1.SetSeed(seed); + + for (int x = 0; x < CS_P; x++) { + for (int y = CS_P - 1; y--;) { + for (int z = 0; z < CS_P; z++) { + float val = ((noise1.GetSimplexFractal(x, y, z)) + 1.0f) / 2.0f; + + if (val > glm::smoothstep(0.15f, 1.0f, (float) y / (float) CS_P)) { + int i = get_zxy_index(x, y, z); + int i_above = get_zxy_index(x, y + 1, z); + + transparentMask[(y * CS_P) + x] |= 1ull << z; + + switch (voxels[i_above]) { + case 0: + voxels[i] = 10; + break; + default: + voxels[i] = 9; + break; + } + } + } + } + } + } + void generateTerrainV2(uint8_t* voxels, uint64_t* opaqueMask, int offsetX, int offsetZ, int seed) { noise2.SetSeed(seed); diff --git a/src/shaders/main.fs b/src/shaders/main.fs index 2f8c02a..46683a2 100644 --- a/src/shaders/main.fs +++ b/src/shaders/main.fs @@ -1,11 +1,11 @@ #version 460 core -layout(location=0) out vec3 out_color; +layout(location=0) out vec4 out_color; in VS_OUT { vec3 pos; flat vec3 normal; - flat vec3 color; + flat vec4 color; } fs_in; uniform vec3 eye_position; @@ -22,8 +22,9 @@ void main() { rim = smoothstep(0.6, 1.0, rim); out_color = - fs_in.color + + vec4(vec3(fs_in.color.rgb + (diffuse_color * max(0, dot(L, fs_in.normal))) + - (rim_color * vec3(rim, rim, rim)) + (rim_color * vec3(rim, rim, rim))), + fs_in.color.a) ; } \ No newline at end of file diff --git a/src/shaders/main.vs b/src/shaders/main.vs index 6cd9444..5642f92 100644 --- a/src/shaders/main.vs +++ b/src/shaders/main.vs @@ -17,7 +17,7 @@ uniform ivec3 eye_position_int; out VS_OUT { out vec3 pos; flat vec3 normal; - flat vec3 color; + flat vec4 color; } vs_out; const vec3 normalLookup[6] = { @@ -29,15 +29,19 @@ const vec3 normalLookup[6] = { vec3( 0, 0, -1 ) }; -const vec3 colorLookup[8] = { - vec3(0.2, 0.659, 0.839), - vec3(0.302, 0.302, 0.302), - vec3(0.278, 0.600, 0.141), - vec3(0.1, 0.1, 0.6), - vec3(0.1, 0.6, 0.6), - vec3(0.6, 0.1, 0.6), - vec3(0.6, 0.6, 0.1), - vec3(0.6, 0.1, 0.1) +const vec4 colorLookup[10] = { + // Opaque colors + vec4(0.2, 0.659, 0.839, 1.0), + vec4(0.302, 0.302, 0.302, 1.0), + vec4(0.278, 0.600, 0.141, 1.0), + vec4(0.1, 0.1, 0.6, 1.0), + vec4(0.1, 0.6, 0.6, 1.0), + vec4(0.6, 0.1, 0.6, 1.0), + vec4(0.6, 0.6, 0.1, 1.0), + vec4(0.6, 0.1, 0.1, 1.0), + // Transparent colors + vec4(0.302, 0.302, 0.302, 0.5), + vec4(0.278, 0.600, 0.141, 0.5), }; const int flipLookup[6] = int[6](1, -1, -1, 1, -1, 1); From 510431fc02e11e01ec9e89584aa6fc2ce9347098 Mon Sep 17 00:00:00 2001 From: Hulitaphy Date: Sat, 9 Aug 2025 16:21:13 -0400 Subject: [PATCH 3/3] Fix transparency when its up against an opaque block --- src/main.cpp | 1 - src/mesher.h | 12 ++++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index bb0fc97..3d3c203 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -203,7 +203,6 @@ void createTestChunk() { case (int) MESH_TYPE::TRANSPARENT_TERRAIN: { noise.generateTerrainV1Transparent(voxels, mainThreadMeshData.transparentMask, 30); - std::cout << "YES\n"; break; } diff --git a/src/mesher.h b/src/mesher.h index 0da3f95..a453309 100644 --- a/src/mesher.h +++ b/src/mesher.h @@ -162,14 +162,14 @@ void mesh(const uint8_t* voxels, MeshData& meshData) { const uint8_t v1 = voxels[abc]; // The transparent step is slower than the opaque step // because we need to check if neighboring transparent blocks are differents (we don't care about that for opaque blocks) - faceMasks[up_faces] |= (static_cast(v1 != voxels[abc + CS_P2]) << (bitPos - 1)); - faceMasks[down_faces] |= (static_cast(v1 != voxels[abc - CS_P2]) << (bitPos - 1)); + faceMasks[up_faces] |= (static_cast(!(opaqueMask[ab + CS_P] & (1ull << bitPos)) && v1 != voxels[abc + CS_P2]) << (bitPos - 1)); + faceMasks[down_faces] |= (static_cast(!(opaqueMask[ab - CS_P] & (1ull << bitPos)) && v1 != voxels[abc - CS_P2]) << (bitPos - 1)); - faceMasks[right_faces] |= (static_cast(v1 != voxels[abc + CS_P]) << (bitPos - 1)); - faceMasks[left_faces] |= (static_cast(v1 != voxels[abc - CS_P]) << (bitPos - 1)); + faceMasks[right_faces] |= (static_cast(!(opaqueMask[ab + 1] & (1ull << bitPos)) && v1 != voxels[abc + CS_P]) << (bitPos - 1)); + faceMasks[left_faces] |= (static_cast(!(opaqueMask[ab - 1] & (1ull << bitPos)) && v1 != voxels[abc - CS_P]) << (bitPos - 1)); - faceMasks[front_faces] |= (static_cast(v1 != voxels[abc + 1]) << bitPos); - faceMasks[back_faces] |= (static_cast(v1 != voxels[abc - 1]) << bitPos); + faceMasks[front_faces] |= (static_cast(!(opaqueMask[ab] & (1ull << (bitPos + 1))) && v1 != voxels[abc + 1]) << bitPos); + faceMasks[back_faces] |= (static_cast(!(opaqueMask[ab] & (1ull << (bitPos - 1))) && v1 != voxels[abc - 1]) << bitPos); } } }