diff --git a/Minecraft.Client/LevelRenderer.cpp b/Minecraft.Client/LevelRenderer.cpp index c850304e..953f6d77 100644 --- a/Minecraft.Client/LevelRenderer.cpp +++ b/Minecraft.Client/LevelRenderer.cpp @@ -65,7 +65,7 @@ #include "../Minecraft.World/SoundTypes.h" #include "FrustumCuller.h" #include "../Minecraft.World/BasicTypeContainers.h" -#include "Common/UI/UIScene_SettingsGraphicsMenu.h" +#include "Common/UI/UIScene_SettingsGraphicsMenu.h" #include "ParticleUtils.h" #include @@ -2584,35 +2584,23 @@ void LevelRenderer::setTilesDirty(int x0, int y0, int z0, int x1, int y1, int z1 setDirty(x0 - 1, y0 - 1, z0 - 1, x1 + 1, y1 + 1, z1 + 1, level); } -static const unsigned char K_CORNER_LUT[8][3] = { - {0, 1, 2}, - {3, 1, 2}, - {0, 4, 2}, - {3, 4, 2}, - {0, 1, 5}, - {3, 1, 5}, - {0, 4, 5}, - {3, 4, 5} -}; - -bool inline clip(const float * __restrict bb, const float * __restrict planes, const unsigned char * __restrict masks) +bool inline clip(float *bb, float *frustum) { - // bb[0]=x0, bb[1]=y0, bb[2]=z0 - // bb[3]=x1, bb[4]=y1, bb[5]=z1 - for (int i = 0; i < 6; i++, planes += 4) - { - float A = planes[0]; - float B = planes[1]; - float C = planes[2]; - float D = planes[3]; + for (int i = 0; i < 6; ++i, frustum += 4) + { + if (frustum[0] * (bb[0]) + frustum[1] * (bb[1]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue; + if (frustum[0] * (bb[3]) + frustum[1] * (bb[1]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue; + if (frustum[0] * (bb[0]) + frustum[1] * (bb[4]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue; + if (frustum[0] * (bb[3]) + frustum[1] * (bb[4]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue; + if (frustum[0] * (bb[0]) + frustum[1] * (bb[1]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue; + if (frustum[0] * (bb[3]) + frustum[1] * (bb[1]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue; + if (frustum[0] * (bb[0]) + frustum[1] * (bb[4]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue; + if (frustum[0] * (bb[3]) + frustum[1] * (bb[4]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue; - const unsigned char* idx = K_CORNER_LUT[masks[i]]; + return false; + } - if (A * bb[idx[0]] + B * bb[idx[1]] + C * bb[idx[2]] + D <= 0.0f) - return false; - } - - return true; + return true; } #ifdef __PS3__ @@ -2692,118 +2680,103 @@ void LevelRenderer::waitForCull_SPU() } #endif // __PS3__ -/* Anaël/vistaslayer.ovh: Rewriting the culler for better performance: - * Note before starting: I chose to use a translator since English isn't my native language and I still lack the technical terminology (even in my own language, actually); - * If I've made any mistakes in the comments, feel free to correct them, I won't be offended :) - * - * The most interesting optimizations are found in the "clip" function. - * The original implementation tests the AABB's 8 vertices against each of the 6 frustum planes. - * - * My implementation uses the sign of each plane's normal to directly select the AABB vertex that maximizes the plane equation. - * Only this vertex needs to be tested; if it lies outside the plane, then the entire AABB is outside. - * This reduces the worst-case scenario to 6 plane equation evaluations—one equation per plane. - * - * As a bonus, I use a lookup table (K_CORNER_LUT) to avoid conditional branching when selecting the vertex, nothing much really. - */ - void LevelRenderer::cull(Culler *culler, float a) { - int playerIndex = mc->player->GetXboxPad(); + int playerIndex = mc->player->GetXboxPad(); // 4J added #if defined __PS3__ && !defined DISABLE_SPU_CODE - cull_SPU(playerIndex, culler, a); - return; -#endif + cull_SPU(playerIndex, culler, a); + return; +#endif // __PS3__ - FrustumCuller *fc = static_cast(culler); - FrustumData *fd = fc->frustum; - float fdraw[6 * 4]; - unsigned char planeMasks[6]; + FrustumCuller *fc = static_cast(culler); + FrustumData *fd = fc->frustum; + float fdraw[6 * 4]; + for( int i = 0; i < 6; i++ ) + { + double fx = fd->m_Frustum[i][0]; + double fy = fd->m_Frustum[i][1]; + double fz = fd->m_Frustum[i][2]; + fdraw[i * 4 + 0] = static_cast(fx); + fdraw[i * 4 + 1] = static_cast(fy); + fdraw[i * 4 + 2] = static_cast(fz); + fdraw[i * 4 + 3] = static_cast(fd->m_Frustum[i][3] + (fx * -fc->xOff) + (fy * -fc->yOff) + (fz * -fc->zOff)); + } - for( int i = 0; i < 6; i++ ) - { - double fx = fd->m_Frustum[i][0]; - double fy = fd->m_Frustum[i][1]; - double fz = fd->m_Frustum[i][2]; + int vis = 0; + int total = 0; + int numWrong = 0; - fdraw[i * 4 + 0] = (float)fx; - fdraw[i * 4 + 1] = (float)fy; - fdraw[i * 4 + 2] = (float)fz; - fdraw[i * 4 + 3] = (float)(fd->m_Frustum[i][3] + ( fx * -fc->xOff ) + ( fy * - fc->yOff ) + ( fz * -fc->zOff )); + // Reset visible chunk lists for this frame + visibleCount_layer0 = 0; + visibleCount_layer1 = 0; + visibleCount_layer2 = 0; + visibleCount_layer3 = 0; - // pre-calculate sign masks for K_CORNER_LUT - planeMasks[i] = ((fx >= 0.0) ? 1 : 0) | - ((fy >= 0.0) ? 2 : 0) | - ((fz >= 0.0) ? 4 : 0); - } + // Column-level frustum culling: test one AABB per XZ column before testing individual Y chunks. + // At dist 64 this reduces ~278K clip() calls to ~17K column tests + per-chunk tests only for visible columns. + for (int x = 0; x < xChunks; x++) + { + for (int z = 0; z < zChunks; z++) + { + // Build column AABB from bottom and top chunks in this column + ClipChunk *bottomChunk = &chunks[playerIndex][(z * yChunks + 0) * xChunks + x]; + ClipChunk *topChunk = &chunks[playerIndex][(z * yChunks + (yChunks - 1)) * xChunks + x]; + float columnAABB[6] = { + bottomChunk->aabb[0], bottomChunk->aabb[1], bottomChunk->aabb[2], // minX, minY, minZ + bottomChunk->aabb[3], topChunk->aabb[4], bottomChunk->aabb[5] // maxX, maxY(top), maxZ + }; - int vis = 0; - int total = 0; + // Test entire column against frustum + if (!clip(columnAABB, fdraw)) + { + // Entire column outside frustum — mark all Y chunks invisible + for (int y = 0; y < yChunks; y++) + { + ClipChunk *pClipChunk = &chunks[playerIndex][(z * yChunks + y) * xChunks + x]; + pClipChunk->visible = false; + } + continue; + } - visibleCount_layer0 = 0; - visibleCount_layer1 = 0; - visibleCount_layer2 = 0; - visibleCount_layer3 = 0; + // Column is (partially) in frustum — test individual chunks + for (int y = 0; y < yChunks; y++) + { + ClipChunk *pClipChunk = &chunks[playerIndex][(z * yChunks + y) * xChunks + x]; + unsigned char flags = pClipChunk->globalIdx == -1 ? 0 : globalChunkFlags[ pClipChunk->globalIdx ]; - for (int x = 0; x < xChunks; x++) - { - for (int z = 0; z < zChunks; z++) - { - ClipChunk *bottomChunk = &chunks[playerIndex][(z * yChunks + 0) * xChunks + x]; - ClipChunk *topChunk = &chunks[playerIndex][(z * yChunks + (yChunks - 1)) * xChunks + x]; + bool clipres = clip(pClipChunk->aabb, fdraw); - float columnAABB[6] = { - bottomChunk->aabb[0], bottomChunk->aabb[1], bottomChunk->aabb[2], - bottomChunk->aabb[3], topChunk->aabb[4], bottomChunk->aabb[5] - }; + if ( (flags & CHUNK_FLAG_COMPILED ) && ( ( flags & CHUNK_FLAG_EMPTYBOTH ) != CHUNK_FLAG_EMPTYBOTH ) ) + { + pClipChunk->visible = clipres; + if( pClipChunk->visible ) vis++; + total++; + } + else if (clipres) + { + pClipChunk->visible = true; + } + else + { + pClipChunk->visible = false; + } - if (!clip(columnAABB, fdraw, planeMasks)) - { - for (int y = 0; y < yChunks; y++) - { - ClipChunk *pClipChunk = &chunks[playerIndex][(z * yChunks + y) * xChunks + x]; - pClipChunk->visible = false; - } - continue; - } - - // the column is in frustum, check chunk by chunk then - for (int y = 0; y < yChunks; y++) - { - ClipChunk *pClipChunk = &chunks[playerIndex][(z * yChunks + y) * xChunks + x]; - unsigned char flags = pClipChunk->globalIdx == -1 ? 0 : globalChunkFlags[ pClipChunk->globalIdx ]; - - bool clipres = clip(pClipChunk->aabb, fdraw, planeMasks); - - if ( (flags & CHUNK_FLAG_COMPILED ) && ( ( flags & CHUNK_FLAG_EMPTYBOTH ) != CHUNK_FLAG_EMPTYBOTH ) ) - { - pClipChunk->visible = clipres; - if( pClipChunk->visible ) vis++; - total++; - } - else if (clipres) - { - pClipChunk->visible = true; - } - else - { - pClipChunk->visible = false; - } - - if (pClipChunk->visible && pClipChunk->globalIdx != -1 && visibleLists_layer0 != nullptr) - { - int list = pClipChunk->globalIdx * CHUNK_RENDER_LAYERS + chunkLists; - if (!((flags & CHUNK_FLAG_EMPTY0) == CHUNK_FLAG_EMPTY0)) - visibleLists_layer0[visibleCount_layer0++] = list; - if (!((flags & CHUNK_FLAG_EMPTY1) == CHUNK_FLAG_EMPTY1)) - visibleLists_layer1[visibleCount_layer1++] = list + 1; - visibleLists_layer2[visibleCount_layer2++] = list + 2; - visibleLists_layer3[visibleCount_layer3++] = list + 3; - } - } - } - } + // Build compact visible chunk lists for renderChunks() + if (pClipChunk->visible && pClipChunk->globalIdx != -1 && visibleLists_layer0 != nullptr) + { + int list = pClipChunk->globalIdx * CHUNK_RENDER_LAYERS + chunkLists; + if (!((flags & CHUNK_FLAG_EMPTY0) == CHUNK_FLAG_EMPTY0)) + visibleLists_layer0[visibleCount_layer0++] = list; + if (!((flags & CHUNK_FLAG_EMPTY1) == CHUNK_FLAG_EMPTY1)) + visibleLists_layer1[visibleCount_layer1++] = list + 1; + visibleLists_layer2[visibleCount_layer2++] = list + 2; + visibleLists_layer3[visibleCount_layer3++] = list + 3; + } + } + } + } } @@ -4159,3 +4132,4 @@ int LevelRenderer::checkAllPresentChunks(bool *faultFound) } return presentCount; } +