diff --git a/Client/MeshPool/MeshDataPool.cs b/Client/MeshPool/MeshDataPool.cs index 3b348369..7bbd334e 100644 --- a/Client/MeshPool/MeshDataPool.cs +++ b/Client/MeshPool/MeshDataPool.cs @@ -368,46 +368,139 @@ public void FrustumCull(FrustumCulling frustumCuller, EnumFrustumCullMode frustu RenderedTriangles = 0; AllocatedTris = 0; - for (int i = 0; i < poolLocations.Count; i++) + // Cache frequently used values ​​outside the loop + int visIdx = ModelDataPoolLocation.VisibleBufIndex; + int count = poolLocations.Count; + ModelDataPoolLocation location; + int size; + + // switch is moved OUTSIDE the loop + switch (frustumCullMode) { - ModelDataPoolLocation location = poolLocations[i]; - - int size = location.IndicesEnd - location.IndicesStart; - if (location.IsVisible(frustumCullMode, frustumCuller)) - { - indicesStartsByte[indicesGroupsCount * 2] = location.IndicesStart * 4; // Offset in bytes, not ints - indicesSizes[indicesGroupsCount] = size; + case EnumFrustumCullMode.CullInstant: + for (int i = 0; i < count; i++) + { + location = poolLocations[i]; + size = location.IndicesEnd - location.IndicesStart; + AllocatedTris += size; + + if (!location.Hide && location.CullVisible[visIdx] && frustumCuller.InFrustum(location.FrustumCullSphere)) + { + indicesStartsByte[indicesGroupsCount * 2] = location.IndicesStart * 4; + indicesSizes[indicesGroupsCount] = size; + RenderedTriangles += size; + indicesGroupsCount++; + } + } + break; - RenderedTriangles += size / 3; + case EnumFrustumCullMode.CullInstantShadowPassNear: + for (int i = 0; i < count; i++) + { + location = poolLocations[i]; + size = location.IndicesEnd - location.IndicesStart; + AllocatedTris += size; + + if (!location.Hide && location.CullVisible[visIdx] && frustumCuller.InFrustumShadowPass(location.FrustumCullSphere)) + { + indicesStartsByte[indicesGroupsCount * 2] = location.IndicesStart * 4; + indicesSizes[indicesGroupsCount] = size; + RenderedTriangles += size; + indicesGroupsCount++; + } + } + break; - indicesGroupsCount++; - } + case EnumFrustumCullMode.CullInstantShadowPassFar: + for (int i = 0; i < count; i++) + { + location = poolLocations[i]; + size = location.IndicesEnd - location.IndicesStart; + AllocatedTris += size; + + if (!location.Hide && location.CullVisible[visIdx] && location.LodLevel >= 1 && frustumCuller.InFrustumShadowPass(location.FrustumCullSphere)) + { + indicesStartsByte[indicesGroupsCount * 2] = location.IndicesStart * 4; + indicesSizes[indicesGroupsCount] = size; + RenderedTriangles += size; + indicesGroupsCount++; + } + } + break; - AllocatedTris += size / 3; + case EnumFrustumCullMode.CullNormal: + for (int i = 0; i < count; i++) + { + location = poolLocations[i]; + size = location.IndicesEnd - location.IndicesStart; + AllocatedTris += size; + + if (!location.Hide && location.CullVisible[visIdx]) + { + bool inFrustum = frustumCuller.InFrustumAndRange(location.FrustumCullSphere, location.FrustumVisible, location.LodLevel); + // UpdateVisibleFlag is inlined directly - removing the extra method call + location.FrustumVisible = inFrustum; + if (inFrustum) + { + indicesStartsByte[indicesGroupsCount * 2] = location.IndicesStart * 4; + indicesSizes[indicesGroupsCount] = size; + RenderedTriangles += size; + indicesGroupsCount++; + } + } + } + break; + + default: // NoCull + for (int i = 0; i < count; i++) + { + location = poolLocations[i]; + size = location.IndicesEnd - location.IndicesStart; + AllocatedTris += size; + + if (!location.Hide) + { + indicesStartsByte[indicesGroupsCount * 2] = location.IndicesStart * 4; + indicesSizes[indicesGroupsCount] = size; + RenderedTriangles += size; + indicesGroupsCount++; + } + } + break; } + + + AllocatedTris = (int)(AllocatedTris / 3); + RenderedTriangles = (int)(RenderedTriangles / 3); } public void SetFullyVisible() { indicesGroupsCount = 0; - RenderedTriangles = 0; AllocatedTris = 0; + ModelDataPoolLocation location; + int size; + for (int i = 0; i < poolLocations.Count; i++) { - ModelDataPoolLocation location = poolLocations[i]; + location = poolLocations[i]; - int size = location.IndicesEnd - location.IndicesStart; + size = location.IndicesEnd - location.IndicesStart; indicesStartsByte[indicesGroupsCount * 2] = location.IndicesStart * 4; // Offset in bytes, not ints indicesSizes[indicesGroupsCount] = size; - RenderedTriangles += size / 3; + RenderedTriangles += size; indicesGroupsCount++; - AllocatedTris += size / 3; + AllocatedTris += size; } + + AllocatedTris = (int)(AllocatedTris / 3); + RenderedTriangles = (int)(RenderedTriangles / 3); + } /// diff --git a/Client/MeshPool/MeshDataPoolManager.cs b/Client/MeshPool/MeshDataPoolManager.cs index bfdde64f..22c29c7c 100644 --- a/Client/MeshPool/MeshDataPoolManager.cs +++ b/Client/MeshPool/MeshDataPoolManager.cs @@ -112,9 +112,12 @@ public ModelDataPoolLocation AddModel(MeshData modeldata, Vec3i modelOrigin, int public void Render(Vec3d playerpos, string originUniformName, EnumFrustumCullMode frustumCullMode = EnumFrustumCullMode.CullNormal) { int count = pools.Count; + MeshDataPool pool; + IShaderProgram currShader; + for (int i = 0; i < count; i++) { - MeshDataPool pool = pools[i]; + pool = pools[i]; if (pool.dimensionId == Dimensions.MiniDimensions) { bool revertModelviewMatrix = false; @@ -132,7 +135,7 @@ public void Render(Vec3d playerpos, string originUniformName, EnumFrustumCullMod FastVec3d renderOffset = dimension.GetRenderOffset(masterPool.currentDt); - var currShader = capi.Render.CurrentActiveShader; + currShader = capi.Render.CurrentActiveShader; if (currShader.HasUniform("modelViewMatrix")) { currShader.UniformMatrix("modelViewMatrix", dimension.GetRenderTransformMatrix(masterPool.currentModelViewMatrix, playerpos)); @@ -162,11 +165,11 @@ public void Render(Vec3d playerpos, string originUniformName, EnumFrustumCullMod finally { if (revertModelviewMatrix) - capi.Render.CurrentActiveShader.UniformMatrix("modelViewMatrix", masterPool.currentModelViewMatrix); + currShader.UniformMatrix("modelViewMatrix", masterPool.currentModelViewMatrix); if (revertMVPMatrix) - capi.Render.CurrentActiveShader.UniformMatrix("mvpMatrix", masterPool.shadowMVPMatrix); + currShader.UniformMatrix("mvpMatrix", masterPool.shadowMVPMatrix); if (revertTransparency) - capi.Render.CurrentActiveShader.Uniform("forcedTransparency", 0f); + currShader.Uniform("forcedTransparency", 0f); } } else @@ -243,3 +246,4 @@ public void GetStats(ref long usedVideoMemory, ref long renderedTris, ref long a } + diff --git a/Client/Render/FrustumCulling.cs b/Client/Render/FrustumCulling.cs index 9a9ac5b3..25458319 100644 --- a/Client/Render/FrustumCulling.cs +++ b/Client/Render/FrustumCulling.cs @@ -28,7 +28,6 @@ public enum EnumFrustumCullMode CullInstantShadowPassFar = 4 } - public struct Plane { public double normalX; @@ -36,6 +35,7 @@ public struct Plane public double normalZ; public double D; private const float SQRT3 = 1.7320508f; + private const float INV_SQRT3 = 0.577350269f; // 1 / √3 /// /// Creates a Plane with normalised (length 1.0) normal vector @@ -60,17 +60,28 @@ public bool AABBisOutside(Sphere sphere) // Optimised algorithm: First, figure out which of the 8 corners of the AABB we are going to test against this plane - if even the "lowest" corner (the corner in the direction most opposed to the plane normal direction) is outside (above) the plane then the whole AABB must be outside the plane // X axis - int sign = normalX > 0 ? 1 : -1; - double testX = (double)sphere.x + sign * sphere.radius / SQRT3; // sphere.radius is 16 if the sphere represents a chunk and chunksize is 32 + //int sign = normalX > 0 ? 1 : -1; + //double testX = (double)sphere.x + sign * sphere.radius / SQRT3; // sphere.radius is 16 if the sphere represents a chunk and chunksize is 32 - sign = normalY > 0 ? 1 : -1; - double testY = (double)sphere.y + sign * sphere.radiusY / SQRT3; + //sign = normalY > 0 ? 1 : -1; + //double testY = (double)sphere.y + sign * sphere.radiusY / SQRT3; - sign = normalZ > 0 ? 1 : -1; - double testZ = (double)sphere.z + sign * sphere.radiusZ / SQRT3; + //sign = normalZ > 0 ? 1 : -1; + //double testZ = (double)sphere.z + sign * sphere.radiusZ / SQRT3; // Now see if that test corner is "outside" the plane - return testX * normalX + testY * normalY + testZ * normalZ + D < 0; + //return testX * normalX + testY * normalY + testZ * normalZ + D < 0; + + + // Sign for each axis (direction of the "worst" vertex) + float signX = normalX > 0 ? INV_SQRT3 : -INV_SQRT3; + float signY = normalY > 0 ? INV_SQRT3 : -INV_SQRT3; + float signZ = normalZ > 0 ? INV_SQRT3 : -INV_SQRT3; + + // Calculate the scalar product directly, without intermediate variables + return (sphere.x + signX * sphere.radius) * normalX + + (sphere.y + signY * sphere.radiusY) * normalY + + (sphere.z + signZ * sphere.radiusZ) * normalZ + D < 0; } } @@ -249,3 +260,4 @@ public class EnumLodPool public const int FarDistanceOnly = 3; } } +