public static ClientChunk ReadChunk(this NetIncomingMessage msg) { Debug.Assert(msg != null); var chunk = new ClientChunk(); // Read all the tiles from the message for (var x = 0; x < Chunk.ChunkSize; x++) { for (var y = 0; y < Chunk.ChunkSize; y++) { // False means no tile, so ignore it if (!msg.ReadBoolean()) { continue; } msg.ReadPadBits(); chunk.Tiles[x][y] = msg.ReadTile(); } } msg.ReadPadBits(); chunk.UpdateColliders(); Debug.Assert(chunk != null); return(chunk); }
public ClientChunk CreateClientChunk(ChunkOctree<ClientChunk> octree, long x, long y, long z) { var existing = octree.Get(x, y, z); if (existing != null) { return existing; } var @new = new ClientChunk(x, y, z); octree.Set(@new); return @new; }
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 OnChunkRecieve(ChunkPacket packet) { var chunkX = packet.X; var chunkY = packet.Y; var data = packet.ChunkData; if (!UnityClient.Map.Chunks.Any(cp => cp.Key.Equals($"{chunkX}_{chunkY}"))) { var chunkParent = new GameObject($"chunk_{chunkX}_{chunkY}"); ClientChunk c = new ClientChunk() { x = chunkX, y = chunkY, GameObject = chunkParent }; Debug.Log("Rendering Chunk " + chunkX + " " + chunkY); for (var x = 0; x < 16; x++) { for (var y = 0; y < 16; y++) { var tileId = data[x, y]; var tilePosition = new Position(chunkX * 16 + x, chunkY * 16 + y); c.Tiles[x, y] = new MapTile(tilePosition, tileId); TileFactory.BuildAndInstantiate(new TileOptions() { Parent = chunkParent, Position = tilePosition, TileId = tileId }); } } // To be a tracked chunk UnityClient.Map.AddChunk(c); } }
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); } }