void TraverseRayAndMarkVisible(Vec3i fromPos, Vec3i toPosRel, double yoffset = 0.5) { ray.origin.Set(fromPos.X + 0.5, fromPos.Y + yoffset, fromPos.Z + 0.5); ray.dir.Set(toPosRel); toPos.Set(fromPos.X + toPosRel.X, fromPos.Y + toPosRel.Y, fromPos.Z + toPosRel.Z); curpos.Set(fromPos); BlockFacing fromFace = null, toFace; int manhattenLength = fromPos.ManhattenDistanceTo(toPos); int curMhDist; while ((curMhDist = curpos.ManhattenDistanceTo(fromPos)) <= manhattenLength + 2) { // Since chunks are arranged in a uniform grid, all we have to do is to find out on // what facing (N/E/S/W/U/D) the ray leaves the chunk and move into that direction. // This may seem inaccurate, but works surpisingly well toFace = GetExitingFace(curpos); if (toFace == null) { return; } long index3d = ((long)curpos.Y * game.WorldMap.chunkMapSizeZFast + curpos.Z) * game.WorldMap.chunkMapSizeXFast + curpos.X; ClientChunk chunk = null; game.WorldMap.chunks.TryGetValue(index3d, out chunk); if (chunk != null) { chunk.SetVisible(true); if (curMhDist > 1 && !chunk.IsTraversable(fromFace, toFace)) { break; } } curpos.Offset(toFace); fromFace = toFace.GetOpposite(); if (!game.WorldMap.IsValidChunkPosFast(curpos.X, curpos.Y, curpos.Z) && (!isAboveHeightLimit || curpos.Y <= 0)) { break; } } }
public void CullInvisibleChunks() { if (!ClientSettings.Occlusionculling || game.WorldMap.chunks.Count < 100) { return; } Vec3d camPos = game.player.Entity.CameraPos; centerpos.Set((int)(camPos.X / chunksize), (int)(camPos.Y / chunksize), (int)(camPos.Z / chunksize)); isAboveHeightLimit = centerpos.Y >= game.WorldMap.ChunkMapSizeY; playerViewVec = EntityPos.GetViewVector(game.mousePitch, game.mouseYaw).Normalize(); lock (game.WorldMap.chunksLock) { foreach (var val in game.WorldMap.chunks) { val.Value.SetVisible(false); } // We sometimes have issues with chunks adjacent to the player getting culled, so lets make these always visible for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { for (int dz = -1; dz <= 1; dz++) { long index3d = game.WorldMap.ChunkIndex3D(dx + centerpos.X, dy + centerpos.Y, dz + centerpos.Z); ClientChunk chunk = null; if (game.WorldMap.chunks.TryGetValue(index3d, out chunk)) { chunk.SetVisible(true); } } } } } // Add some 15 extra degrees to the field of view angle for safety float fov = GameMath.Cos(game.MainCamera.Fov + 15 * GameMath.DEG2RAD); for (int i = 0; i < cubicShellPositions.Length; i++) { Vec3i vec = cubicShellPositions[i]; float dotProd = playerViewVec.Dot(cubicShellPositionsNormalized[i]); if (dotProd <= fov / 2) { // Outside view frustum continue; } // It seems that one trace can cause issues where chunks are culled when they shouldn't // 2 traces with a y-offset seems to mitigate most of that issue TraverseRayAndMarkVisible(centerpos, vec, 0.25); TraverseRayAndMarkVisible(centerpos, vec, 0.75); } }