static void QuickSort( int[] keys, ChunkInfo[] values, int left, int right ) { while( left < right ) { int i = left, j = right; int pivot = keys[(i + j) / 2]; // partition the list while( i <= j ) { while( pivot > keys[i] ) i++; while( pivot < keys[j] ) j--; if( i <= j ) { int key = keys[i]; keys[i] = keys[j]; keys[j] = key; ChunkInfo value = values[i]; values[i] = values[j]; values[j] = value; i++; j--; } } // recurse into the smaller subset if( j - left <= right - i ) { if( left < j ) QuickSort( keys, values, left, j ); left = i; } else { if( i < right ) QuickSort( keys, values, i, right ); right = j; } } }
int UpdateChunksAndVisibility(double deltaTime, ref int chunkUpdats, int adjViewDistSqr) { ChunkInfo[] chunks = renderer.chunks, render = renderer.renderChunks; int j = 0; for (int i = 0; i < chunks.Length; i++) { ChunkInfo info = chunks[i]; if (info.Empty) { continue; } int distSqr = distances[i]; bool noData = info.NormalParts == null && info.TranslucentParts == null; bool inRange = distSqr <= adjViewDistSqr; if (!noData && distSqr >= adjViewDistSqr + 32 * 16) { DeleteChunk(info, true); continue; } if (noData && inRange && chunkUpdats < chunksTarget) { BuildChunk(info, ref chunkUpdats); } info.Visible = inRange && game.Culling.SphereInFrustum(info.CentreX, info.CentreY, info.CentreZ, 14); // 14 ~ sqrt(3 * 8^2) if (info.Visible && !info.Empty) { render[j] = info; j++; } } return(j); }
void DrawTranslucentPart(ChunkInfo info, ref ChunkPartInfo part, int m) { gfx.BindVb(part.VbId); bool drawLeft = (inTranslucent || info.DrawLeft) && part.LeftCount > 0; bool drawRight = (inTranslucent || info.DrawRight) && part.RightCount > 0; bool drawBottom = (inTranslucent || info.DrawBottom) && part.BottomCount > 0; bool drawTop = (inTranslucent || info.DrawTop) && part.TopCount > 0; bool drawFront = (inTranslucent || info.DrawFront) && part.FrontCount > 0; bool drawBack = (inTranslucent || info.DrawBack) && part.BackCount > 0; if (drawLeft && drawRight) { gfx.DrawIndexedVb_TrisT2fC4b(part.LeftCount + part.RightCount, part.LeftIndex); game.Vertices += m * (part.LeftCount + part.RightCount); } else if (drawLeft) { gfx.DrawIndexedVb_TrisT2fC4b(part.LeftCount, part.LeftIndex); game.Vertices += m * part.LeftCount; } else if (drawRight) { gfx.DrawIndexedVb_TrisT2fC4b(part.RightCount, part.RightIndex); game.Vertices += m * part.RightCount; } if (drawFront && drawBack) { gfx.DrawIndexedVb_TrisT2fC4b(part.FrontCount + part.BackCount, part.FrontIndex); game.Vertices += m * (part.FrontCount + part.BackCount); } else if (drawFront) { gfx.DrawIndexedVb_TrisT2fC4b(part.FrontCount, part.FrontIndex); game.Vertices += m * part.FrontCount; } else if (drawBack) { gfx.DrawIndexedVb_TrisT2fC4b(part.BackCount, part.BackIndex); game.Vertices += m * part.BackCount; } if (drawBottom && drawTop) { gfx.DrawIndexedVb_TrisT2fC4b(part.BottomCount + part.TopCount, part.BottomIndex); game.Vertices += m * (part.BottomCount + part.TopCount); } else if (drawBottom) { gfx.DrawIndexedVb_TrisT2fC4b(part.BottomCount, part.BottomIndex); game.Vertices += m * part.BottomCount; } else if (drawTop) { gfx.DrawIndexedVb_TrisT2fC4b(part.TopCount, part.TopIndex); game.Vertices += m * part.TopCount; } }
void RenderNormalBatch(int batch) { for (int i = 0; i < chunks.Length; i++) { ChunkInfo info = chunks[i]; #if OCCLUSION if (info.NormalParts == null || !info.Visible || info.Occluded) { continue; } #else if (info.NormalParts == null || !info.Visible) { continue; } #endif ChunkPartInfo part = info.NormalParts[batch]; if (part.IndicesCount == 0) { continue; } usedNormal[batch] = true; if (part.IndicesCount > maxIndices) { DrawBigPart(info, ref part); } else { DrawPart(info, ref part); } if (part.SpriteCount > 0) { int groupCount = part.SpriteCount / 4; api.FaceCulling = true; if (info.DrawRight || info.DrawFront) { api.DrawIndexedVb_TrisT2fC4b(groupCount, 0); } if (info.DrawLeft || info.DrawBack) { api.DrawIndexedVb_TrisT2fC4b(groupCount, groupCount); } if (info.DrawLeft || info.DrawFront) { api.DrawIndexedVb_TrisT2fC4b(groupCount, groupCount * 2); } if (info.DrawRight || info.DrawBack) { api.DrawIndexedVb_TrisT2fC4b(groupCount, groupCount * 3); } api.FaceCulling = false; } game.Vertices += part.IndicesCount; } }
void Print(string prefix, int cx, int cy, int cz) { ChunkInfo chunk = unsortedChunks[cx + chunksX * (cy + cz * chunksY)]; Console.WriteLine("{0} D {1}: V {2}, O {3}, {4}", prefix, chunk.DistanceFlags, chunk.OccludedFlags, chunk.OcclusionFlags, chunk.Occluded); // Console.WriteLine(chunk.DistanceFlags + " : " + chunk.OccludedFlags + " : " + chunk.OcclusionFlags + " , " + chunk.Occluded); Vector3I p = chunkPos; int x1 = chunk.CentreX - 8, x2 = chunk.CentreX + 8; int y1 = chunk.CentreY - 8, y2 = chunk.CentreY + 8; int z1 = chunk.CentreZ - 8, z2 = chunk.CentreZ + 8; int dx = Math.Max(x1 - p.X, Math.Max(0, p.X - x2)); int dy = Math.Max(y1 - p.Y, Math.Max(0, p.Y - y2)); int dz = Math.Max(z1 - p.Z, Math.Max(0, p.Z - z2)); int distX, distY, distZ; // X axis collisions int dxLeft = Math.Abs(x1 - p.X), dxRight = Math.Abs(x2 - p.X); if (dxLeft < dxRight) { distX = dxLeft * dxLeft + dy * dy + dz * dz; } else { distX = dxRight * dxRight + dy * dy + dz * dz; } // Z axis collisions int dxFront = Math.Abs(z1 - p.Z), dxBack = Math.Abs(z2 - p.Z); if (dxFront < dxBack) { distZ = dx * dx + dy * dy + dxFront * dxFront; } else { distZ = dx * dx + dy * dy + dxBack * dxBack; } // Y axis collisions int dxBottom = Math.Abs(y1 - p.Y), dxTop = Math.Abs(y2 - p.Y); if (dxBottom < dxTop) { distY = dx * dx + dxBottom * dxBottom + dz * dz; } else { distY = dx * dx + dxTop * dxTop + dz * dz; } int distMin = Math.Min(distX, Math.Min(distY, distZ)); Console.WriteLine(" M {0} : X {1}, Y {2}, Z {3} ({4}, {5})", distMin, distX, distY, distZ, dxFront, dxBack); }
void DeleteChunk(ChunkInfo info, bool decUsed) { info.Empty = false; #if OCCLUSION info.OcclusionFlags = 0; info.OccludedFlags = 0; #endif DeleteData(ref info.NormalParts, decUsed); DeleteData(ref info.TranslucentParts, decUsed); }
void DrawPart(ChunkInfo info, ref ChunkPartInfo part) { api.BindVb(part.VbId); bool drawLeft = info.DrawLeft && part.LeftCount > 0; bool drawRight = info.DrawRight && part.RightCount > 0; bool drawBottom = info.DrawBottom && part.BottomCount > 0; bool drawTop = info.DrawTop && part.TopCount > 0; bool drawFront = info.DrawFront && part.FrontCount > 0; bool drawBack = info.DrawBack && part.BackCount > 0; if (drawLeft && drawRight) { api.FaceCulling = true; api.DrawIndexedVb_TrisT2fC4b(part.LeftCount + part.RightCount, part.LeftIndex); api.FaceCulling = false; } else if (drawLeft) { api.DrawIndexedVb_TrisT2fC4b(part.LeftCount, part.LeftIndex); } else if (drawRight) { api.DrawIndexedVb_TrisT2fC4b(part.RightCount, part.RightIndex); } if (drawFront && drawBack) { api.FaceCulling = true; api.DrawIndexedVb_TrisT2fC4b(part.FrontCount + part.BackCount, part.FrontIndex); api.FaceCulling = false; } else if (drawFront) { api.DrawIndexedVb_TrisT2fC4b(part.FrontCount, part.FrontIndex); } else if (drawBack) { api.DrawIndexedVb_TrisT2fC4b(part.BackCount, part.BackIndex); } if (drawBottom && drawTop) { api.FaceCulling = true; api.DrawIndexedVb_TrisT2fC4b(part.BottomCount + part.TopCount, part.BottomIndex); api.FaceCulling = false; } else if (drawBottom) { api.DrawIndexedVb_TrisT2fC4b(part.BottomCount, part.BottomIndex); } else if (drawTop) { api.DrawIndexedVb_TrisT2fC4b(part.TopCount, part.TopIndex); } }
public void Enqueue(ChunkInfo item) { if (Size == array.Length) { throw new InvalidOperationException("Queue limit reached"); } array[tail] = item; tail = (tail + 1) % array.Length; Size++; }
void DeleteChunk(ChunkInfo info) { info.Empty = false; info.AllAir = false; #if OCCLUSION info.OcclusionFlags = 0; info.OccludedFlags = 0; #endif if (info.NormalParts != null) { ChunkPartInfo[] parts = info.NormalParts; for (int i = 0; i < parts.Length; i++) { game.Graphics.DeleteVb(ref parts[i].VbId); if (parts[i].VerticesCount == 0) { continue; } renderer.normalPartsCount[i]--; } info.NormalParts = null; } if (info.LiquidParts != null) { ChunkPartInfo[] parts = info.LiquidParts; for (int i = 0; i < parts.Length; i++) { game.Graphics.DeleteVb(ref parts[i].VbId); if (parts[i].VerticesCount == 0) { continue; } renderer.liquidPartsCount[i]--; } info.LiquidParts = null; } if (info.TranslucentParts != null) { ChunkPartInfo[] parts = info.TranslucentParts; for (int i = 0; i < parts.Length; i++) { game.Graphics.DeleteVb(ref parts[i].VbId); if (parts[i].VerticesCount == 0) { continue; } renderer.translucentPartsCount[i]--; } info.TranslucentParts = null; } }
void QueueChunk(int cx, int cy, int cz, ChunkQueue queue) { if (cx >= 0 && cy >= 0 && cz >= 0 && cx < chunksX && cy < chunksY && cz < chunksZ) { ChunkInfo info = unsortedChunks[cx + chunksX * (cy + cz * chunksY)]; if (!info.Visited) { queue.Enqueue(info); } info.Visited = true; } }
public static void UpdateSortOrder(Game game, ChunkUpdater updater) { Vector3 cameraPos = game.CurrentCameraPos; //int adjCamX, adjCamZ; //game.Camera.GetCamChunk(out adjCamX, out adjCamZ); //cameraPos.X -= adjCamX; //cameraPos.Z -= adjCamZ; Vector3I newChunkPos = Vector3I.Floor(cameraPos); newChunkPos.X = (newChunkPos.X & ~0x0F) + 8; newChunkPos.Y = (newChunkPos.Y & ~0x0F) + 8; newChunkPos.Z = (newChunkPos.Z & ~0x0F) + 8; if (newChunkPos == updater.chunkPos) { return; } ChunkInfo[] chunks = game.MapRenderer.chunks; int[] distances = updater.distances; Vector3I pPos = newChunkPos; updater.chunkPos = pPos; for (int i = 0; i < chunks.Length; i++) { ChunkInfo info = chunks[i]; // Calculate distance to chunk centre int dx = info.CentreX - pPos.X, dy = info.CentreY - pPos.Y, dz = info.CentreZ - pPos.Z; distances[i] = dx * dx + dy * dy + dz * dz; // Can work out distance to chunk faces as offset from distance to chunk centre on each axis. int dXMin = dx - 8, dXMax = dx + 8; int dYMin = dy - 8, dYMax = dy + 8; int dZMin = dz - 8, dZMax = dz + 8; // Back face culling: make sure that the chunk is definitely entirely back facing. info.DrawLeft = !(dXMin <= 0 && dXMax <= 0); info.DrawRight = !(dXMin >= 0 && dXMax >= 0); info.DrawFront = !(dZMin <= 0 && dZMax <= 0); info.DrawBack = !(dZMin >= 0 && dZMax >= 0); info.DrawBottom = !(dYMin <= 0 && dYMax <= 0); info.DrawTop = !(dYMin >= 0 && dYMax >= 0); } // NOTE: Over 5x faster compared to normal comparison of IComparer<ChunkInfo>.Compare if (distances.Length > 1) { QuickSort(distances, chunks, 0, chunks.Length - 1); } updater.ResetUsedFlags(); //SimpleOcclusionCulling(); }
void SimpleOcclusionCulling() // TODO: still broken { Vector3 p = game.LocalPlayer.EyePosition; Vector3I mapLoc = Vector3I.Floor(p); Utils.Clamp(ref mapLoc.X, 0, game.World.Width - 1); Utils.Clamp(ref mapLoc.Y, 0, game.World.Height - 1); Utils.Clamp(ref mapLoc.Z, 0, game.World.Length - 1); int cx = mapLoc.X >> 4; int cy = mapLoc.Y >> 4; int cz = mapLoc.Z >> 4; ChunkInfo chunkIn = unsortedChunks[cx + chunksX * (cy + cz * chunksY)]; byte chunkInOcclusionFlags = chunkIn.OcclusionFlags; chunkIn.OcclusionFlags = 0; ChunkQueue queue = new ChunkQueue(chunksX * chunksY * chunksZ); for (int i = 0; i < chunks.Length; i++) { ChunkInfo chunk = chunks[i]; chunk.Visited = false; chunk.Occluded = false; chunk.OccludedFlags = chunk.OcclusionFlags; chunk.DistanceFlags = 0; } chunkIn.Visited = true; mapLoc = Vector3I.Floor(p); if (game.World.IsValidPos(mapLoc)) { chunkIn.DistanceFlags = flagX | flagY | flagZ; } else { chunkIn.OccludedFlags = chunkIn.OcclusionFlags = chunkInOcclusionFlags; chunkIn.DistanceFlags |= (mapLoc.X < 0 || mapLoc.X >= game.World.Width) ? flagX : (byte)0; chunkIn.DistanceFlags |= (mapLoc.Y < 0 || mapLoc.Y >= game.World.Height) ? flagY : (byte)0; chunkIn.DistanceFlags |= (mapLoc.Z < 0 || mapLoc.Z >= game.World.Length) ? flagZ : (byte)0; } Console.WriteLine("SRC {0}", cx + "," + cy + "," + cz); QueueChunk(cx - 1, cy, cz, queue); QueueChunk(cx + 1, cy, cz, queue); QueueChunk(cx, cy - 1, cz, queue); QueueChunk(cx, cy + 1, cz, queue); QueueChunk(cx, cy, cz - 1, queue); QueueChunk(cx, cy, cz + 1, queue); ProcessQueue(queue); chunkIn.OcclusionFlags = chunkInOcclusionFlags; }
public static void UpdateSortOrder(Game game, ChunkUpdater updater) { Vector3 cameraPos = game.CurrentCameraPos; Vector3I newChunkPos = Vector3I.Floor(cameraPos); newChunkPos.X = (newChunkPos.X & ~0x0F) + 8; newChunkPos.Y = (newChunkPos.Y & ~0x0F) + 8; newChunkPos.Z = (newChunkPos.Z & ~0x0F) + 8; if (newChunkPos == updater.chunkPos) { return; } ChunkInfo[] chunks = game.MapRenderer.chunks; int[] distances = updater.distances; updater.chunkPos = newChunkPos; for (int i = 0; i < distances.Length; i++) { ChunkInfo info = chunks[i]; distances[i] = Utils.DistanceSquared(info.CentreX, info.CentreY, info.CentreZ, updater.chunkPos.X, updater.chunkPos.Y, updater.chunkPos.Z); } // NOTE: Over 5x faster compared to normal comparison of IComparer<ChunkInfo>.Compare if (distances.Length > 1) { QuickSort(distances, chunks, 0, chunks.Length - 1); } Vector3I pPos = newChunkPos; for (int i = 0; i < chunks.Length; i++) { ChunkInfo info = chunks[i]; int dX1 = (info.CentreX - 8) - pPos.X, dX2 = (info.CentreX + 8) - pPos.X; int dY1 = (info.CentreY - 8) - pPos.Y, dY2 = (info.CentreY + 8) - pPos.Y; int dZ1 = (info.CentreZ - 8) - pPos.Z, dZ2 = (info.CentreZ + 8) - pPos.Z; // Back face culling: make sure that the chunk is definitely entirely back facing. info.DrawLeft = !(dX1 <= 0 && dX2 <= 0); info.DrawRight = !(dX1 >= 0 && dX2 >= 0); info.DrawFront = !(dZ1 <= 0 && dZ2 <= 0); info.DrawBack = !(dZ1 >= 0 && dZ2 >= 0); info.DrawBottom = !(dY1 <= 0 && dY2 <= 0); info.DrawTop = !(dY1 >= 0 && dY2 >= 0); } updater.RecalcBooleans(false); //SimpleOcclusionCulling(); }
void OccludeZ( int cx, int cy, int cz, int zOffset, ChunkInfo info ) { cz += zOffset; if( cz >= 0 && cz < chunksZ ) { ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)]; if( (neighbour.OccludedFlags & neighbour.DistanceFlags) != neighbour.DistanceFlags ) info.Occluded = false; else info.OccludedFlags |= flagZ; } else { info.Occluded = false; } info.DistanceFlags |= flagZ; }
void OccludeY( int cx, int cy, int cz, int yOffset, ChunkInfo info ) { cy += yOffset; if( cy >= 0 && cy < chunksY ) { ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)]; if( (neighbour.OccludedFlags & neighbour.DistanceFlags) != neighbour.DistanceFlags ) info.Occluded = false; else info.OccludedFlags |= flagY; } else { info.Occluded = false; } info.DistanceFlags |= flagY; }
void OccludeX( int cx, int cy, int cz, int xOffset, ChunkInfo info ) { cx += xOffset; if( cx >= 0 && cx < chunksX ) { ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)]; if( (neighbour.OccludedFlags & neighbour.DistanceFlags) != neighbour.DistanceFlags ) info.Occluded = false; else info.OccludedFlags |= flagX; } else { info.Occluded = false; } info.DistanceFlags |= flagX; }
void BuildChunk(ChunkInfo info, ref int chunkUpdates) { game.ChunkUpdates++; chunkUpdates++; info.PendingDelete = false; builder.MakeChunk(info); if (info.NormalParts == null && info.LiquidParts == null && info.TranslucentParts == null) { info.Empty = true; return; } if (info.NormalParts != null) { ChunkPartInfo[] parts = info.NormalParts; for (int i = 0; i < parts.Length; i++) { if (parts[i].VerticesCount == 0) { continue; } renderer.normalPartsCount[i]++; } } if (info.LiquidParts != null) { ChunkPartInfo[] parts = info.LiquidParts; for (int i = 0; i < parts.Length; i++) { if (parts[i].VerticesCount == 0) { continue; } renderer.liquidPartsCount[i]++; } } if (info.TranslucentParts != null) { ChunkPartInfo[] parts = info.TranslucentParts; for (int i = 0; i < parts.Length; i++) { if (parts[i].VerticesCount == 0) { continue; } renderer.translucentPartsCount[i]++; } } }
public ChunkInfo Dequeue() { if (Size == 0) { throw new InvalidOperationException("No elements left in queue"); } ChunkInfo item = array[head]; array[head] = null; head = (head + 1) % array.Length; Size--; return(item); }
void RenderNormalBatch(int batch) { for (int i = 0; i < renderCount; i++) { ChunkInfo info = renderChunks[i]; if (info.NormalParts == null) { continue; } ChunkPartInfo part = info.NormalParts[batch]; if (part.IndicesCount == 0) { continue; } usedNormal[batch] = true; if (part.IndicesCount > maxIndices) { DrawBigPart(info, ref part); } else { DrawPart(info, ref part); } if (part.SpriteCount > 0) { int count = part.SpriteCount / 4; // 4 per sprite gfx.FaceCulling = true; if (info.DrawRight || info.DrawFront) { gfx.DrawIndexedVb_TrisT2fC4b(count, 0); game.Vertices += count; } if (info.DrawLeft || info.DrawBack) { gfx.DrawIndexedVb_TrisT2fC4b(count, count); game.Vertices += count; } if (info.DrawLeft || info.DrawFront) { gfx.DrawIndexedVb_TrisT2fC4b(count, count * 2); game.Vertices += count; } if (info.DrawRight || info.DrawBack) { gfx.DrawIndexedVb_TrisT2fC4b(count, count * 3); game.Vertices += count; } gfx.FaceCulling = false; } } }
void DrawTranslucentPart(ChunkInfo info, ref ChunkPartInfo part) { api.BindVb(part.VbId); bool drawLeft = (drawAllFaces || info.DrawLeft) && part.LeftCount > 0; bool drawRight = (drawAllFaces || info.DrawRight) && part.RightCount > 0; bool drawBottom = (drawAllFaces || info.DrawBottom) && part.BottomCount > 0; bool drawTop = (drawAllFaces || info.DrawTop) && part.TopCount > 0; bool drawFront = (drawAllFaces || info.DrawFront) && part.FrontCount > 0; bool drawBack = (drawAllFaces || info.DrawBack) && part.BackCount > 0; if (drawLeft && drawRight) { api.DrawIndexedVb_TrisT2fC4b(part.LeftCount + part.RightCount, part.LeftIndex); } else if (drawLeft) { api.DrawIndexedVb_TrisT2fC4b(part.LeftCount, part.LeftIndex); } else if (drawRight) { api.DrawIndexedVb_TrisT2fC4b(part.RightCount, part.RightIndex); } if (drawFront && drawBack) { api.DrawIndexedVb_TrisT2fC4b(part.FrontCount + part.BackCount, part.FrontIndex); } else if (drawFront) { api.DrawIndexedVb_TrisT2fC4b(part.FrontCount, part.FrontIndex); } else if (drawBack) { api.DrawIndexedVb_TrisT2fC4b(part.BackCount, part.BackIndex); } if (drawBottom && drawTop) { api.DrawIndexedVb_TrisT2fC4b(part.BottomCount + part.TopCount, part.BottomIndex); } else if (drawBottom) { api.DrawIndexedVb_TrisT2fC4b(part.BottomCount, part.BottomIndex); } else if (drawTop) { api.DrawIndexedVb_TrisT2fC4b(part.TopCount, part.TopIndex); } }
public void MarkDeleteChunk(int cx, int cy, int cz) { if (cx < 0 || cy < 0 || cz < 0 || cx >= chunksX || cy >= chunksY || cz >= chunksZ) { return; } ChunkInfo info = unsortedChunks[cx + chunksX * (cy + cz * chunksY)]; //if (info.AllAir) return; // do not recreate chunks completely air info.Empty = false; info.PendingDelete = true; //info.TranslucentParts = null; //info.NormalParts = null; //info.LiquidParts = null; }
void BuildChunk(ChunkInfo info, ref int chunkUpdates) { game.ChunkUpdates++; builder.GetDrawInfo(info.CentreX - 8, info.CentreY - 8, info.CentreZ - 8, ref info.NormalParts, ref info.TranslucentParts); if (info.NormalParts == null && info.TranslucentParts == null) { info.Empty = true; } else { IncrementUsed(info.NormalParts); IncrementUsed(info.TranslucentParts); } chunkUpdates++; }
/// <summary> Marks the given chunk as needing to be deleted. </summary> public void RefreshChunk(int cx, int cy, int cz) { if (cx < 0 || cy < 0 || cz < 0 || cx >= chunksX || cy >= chunksY || cz >= chunksZ) { return; } ChunkInfo info = unsortedChunks[cx + chunksX * (cy + cz * chunksY)]; if (info.AllAir) { return; // do not recreate chunks completely air } info.Empty = false; info.PendingDelete = true; }
public void RedrawBlock(int x, int y, int z, BlockID block, int oldHeight, int newHeight) { int cx = x >> 4, cy = y >> 4, cz = z >> 4; // Does this chunk now contain air? ChunkInfo curInfo = renderer.unsortedChunks[cx + chunksX * (cy + cz * chunksY)]; curInfo.AllAir &= game.BlockInfo.Draw[block] == DrawType.Gas; // NOTE: It's a lot faster to only update the chunks that are affected by the change in shadows, // rather than the entire column. int newCy = newHeight < 0 ? 0 : newHeight >> 4; int oldCy = oldHeight < 0 ? 0 : oldHeight >> 4; int minCy = Math.Min(oldCy, newCy), maxCy = Math.Max(oldCy, newCy); ResetColumn(cx, cy, cz, minCy, maxCy); World world = game.World; int bX = x & 0x0F, bY = y & 0x0F, bZ = z & 0x0F; if (bX == 0 && cx > 0) { ResetNeighbour(x - 1, y, z, block, cx - 1, cy, cz, minCy, maxCy); } if (bY == 0 && cy > 0 && Needs(block, world.GetBlock(x, y - 1, z))) { ResetChunk(cx, cy - 1, cz); } if (bZ == 0 && cz > 0) { ResetNeighbour(x, y, z - 1, block, cx, cy, cz - 1, minCy, maxCy); } if (bX == 15 && cx < chunksX - 1) { ResetNeighbour(x + 1, y, z, block, cx + 1, cy, cz, minCy, maxCy); } if (bY == 15 && cy < chunksY - 1 && Needs(block, world.GetBlock(x, y + 1, z))) { ResetChunk(cx, cy + 1, cz); } if (bZ == 15 && cz < chunksZ - 1) { ResetNeighbour(x, y, z + 1, block, cx, cy, cz + 1, minCy, maxCy); } }
void DeleteChunk(ChunkInfo info) { info.Empty = false; info.AllAir = false; #if OCCLUSION info.OcclusionFlags = 0; info.OccludedFlags = 0; #endif #if !GL11 game.Graphics.DeleteVb(ref info.Vb); #endif if (info.NormalParts != null) { ChunkPartInfo[] parts = info.NormalParts; for (int i = 0; i < parts.Length; i++) { if (parts[i].Offset < 0) { continue; } renderer.normalPartsCount[i]--; #if GL11 game.Graphics.DeleteVb(ref parts[i].Vb); #endif } info.NormalParts = null; } if (info.TranslucentParts != null) { ChunkPartInfo[] parts = info.TranslucentParts; for (int i = 0; i < parts.Length; i++) { if (parts[i].Offset < 0) { continue; } renderer.translucentPartsCount[i]--; #if GL11 game.Graphics.DeleteVb(ref parts[i].Vb); #endif } info.TranslucentParts = null; } }
static void QuickSort(int[] keys, ChunkInfo[] values, int left, int right) { while (left < right) { int i = left, j = right; int pivot = keys[(i + j) / 2]; // partition the list while (i <= j) { while (pivot > keys[i]) { i++; } while (pivot < keys[j]) { j--; } if (i <= j) { int key = keys[i]; keys[i] = keys[j]; keys[j] = key; ChunkInfo value = values[i]; values[i] = values[j]; values[j] = value; i++; j--; } } // recurse into the smaller subset if (j - left <= right - i) { if (left < j) { QuickSort(keys, values, left, j); } left = i; } else { if (i < right) { QuickSort(keys, values, i, right); } right = j; } } }
void RenderTranslucentBatch(int batch) { for (int i = 0; i < renderCount; i++) { ChunkInfo info = renderChunks[i]; if (info.TranslucentParts == null) { continue; } ChunkPartInfo part = info.TranslucentParts[batch]; if (part.IndicesCount == 0) { continue; } DrawTranslucentPart(info, ref part, 1); } }
int UpdateChunksStill(ref int chunkUpdates) { ChunkInfo[] chunks = renderer.chunks, render = renderer.renderChunks; int j = 0; int viewDistSqr = AdjustViewDist(game.ViewDistance); int userDistSqr = AdjustViewDist(game.UserViewDistance); for (int i = 0; i < chunks.Length; i++) { ChunkInfo info = chunks[i]; if (info.Empty) { continue; } int distSqr = distances[i]; bool noData = info.NormalParts == null && info.LiquidParts == null && info.TranslucentParts == null; if (!noData && distSqr >= userDistSqr + 32 * 16) { DeleteChunk(info); continue; } noData |= info.PendingDelete; if (noData && distSqr <= userDistSqr && chunkUpdates < chunksTarget) { DeleteChunk(info); BuildChunk(info, ref chunkUpdates); // only need to update the visibility of chunks in range. info.Visible = distSqr <= viewDistSqr && game.Culling.SphereInFrustum(info.CentreX, info.CentreY, info.CentreZ, 14); // 14 ~ sqrt(3 * 8^2) if (info.Visible && !info.Empty) { render[j] = info; j++; } } else if (info.Visible) { render[j] = info; j++; } } return(j); }
void OccludeZ(int cx, int cy, int cz, int zOffset, ChunkInfo info) { cz += zOffset; if (cz >= 0 && cz < chunksZ) { ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)]; if ((neighbour.OccludedFlags & neighbour.DistanceFlags) != neighbour.DistanceFlags) { info.Occluded = false; } else { info.OccludedFlags |= flagZ; } } else { info.Occluded = false; } info.DistanceFlags |= flagZ; }
void OccludeY(int cx, int cy, int cz, int yOffset, ChunkInfo info) { cy += yOffset; if (cy >= 0 && cy < chunksY) { ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)]; if ((neighbour.OccludedFlags & neighbour.DistanceFlags) != neighbour.DistanceFlags) { info.Occluded = false; } else { info.OccludedFlags |= flagY; } } else { info.Occluded = false; } info.DistanceFlags |= flagY; }
void OccludeX(int cx, int cy, int cz, int xOffset, ChunkInfo info) { cx += xOffset; if (cx >= 0 && cx < chunksX) { ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)]; if ((neighbour.OccludedFlags & neighbour.DistanceFlags) != neighbour.DistanceFlags) { info.Occluded = false; } else { info.OccludedFlags |= flagX; } } else { info.Occluded = false; } info.DistanceFlags |= flagX; }
void UpdateChunksAndVisibility(double deltaTime, ref int chunkUpdats, int adjViewDistSqr) { ChunkInfo[] chunks = renderer.chunks; for (int i = 0; i < chunks.Length; i++) { ChunkInfo info = chunks[i]; if (info.Empty) { continue; } int distSqr = distances[i]; bool inRange = distSqr <= adjViewDistSqr; if (info.NormalParts == null && info.TranslucentParts == null) { if (inRange && chunkUpdats < chunksTarget) { BuildChunk(info, ref chunkUpdats); } } info.Visible = inRange && game.Culling.SphereInFrustum(info.CentreX, info.CentreY, info.CentreZ, 14); // 14 ~ sqrt(3 * 8^2) } }
void BuildChunk(ChunkInfo info, ref int chunkUpdates) { game.ChunkUpdates++; builder.GetDrawInfo(info.CentreX - 8, info.CentreY - 8, info.CentreZ - 8, ref info.NormalParts, ref info.TranslucentParts, ref info.AllAir); info.PendingDelete = false; if (info.NormalParts == null && info.TranslucentParts == null) { info.Empty = true; } else { if (info.NormalParts != null) { IncrementUsed(info.NormalParts); } if (info.TranslucentParts != null) { IncrementUsed(info.TranslucentParts); } } chunkUpdates++; }
void DrawPart( ChunkInfo info, ref ChunkPartInfo part ) { api.BindVb( part.VbId ); bool drawLeft = info.DrawLeft && part.LeftCount > 0; bool drawRight = info.DrawRight && part.RightCount > 0; bool drawBottom = info.DrawBottom && part.BottomCount > 0; bool drawTop = info.DrawTop && part.TopCount > 0; bool drawFront = info.DrawFront && part.FrontCount > 0; bool drawBack = info.DrawBack && part.BackCount > 0; if( drawLeft && drawRight ) { api.FaceCulling = true; api.DrawIndexedVb_TrisT2fC4b( part.LeftCount + part.RightCount, part.LeftIndex ); api.FaceCulling = false; } else if( drawLeft ) { api.DrawIndexedVb_TrisT2fC4b( part.LeftCount, part.LeftIndex ); } else if( drawRight ) { api.DrawIndexedVb_TrisT2fC4b( part.RightCount, part.RightIndex ); } if( drawFront && drawBack ) { api.FaceCulling = true; api.DrawIndexedVb_TrisT2fC4b( part.FrontCount + part.BackCount, part.FrontIndex ); api.FaceCulling = false; } else if( drawFront ) { api.DrawIndexedVb_TrisT2fC4b( part.FrontCount, part.FrontIndex ); } else if( drawBack ) { api.DrawIndexedVb_TrisT2fC4b( part.BackCount, part.BackIndex ); } if( drawBottom && drawTop ) { api.FaceCulling = true; api.DrawIndexedVb_TrisT2fC4b( part.BottomCount + part.TopCount, part.BottomIndex ); api.FaceCulling = false; } else if( drawBottom ) { api.DrawIndexedVb_TrisT2fC4b( part.BottomCount, part.BottomIndex ); } else if( drawTop ) { api.DrawIndexedVb_TrisT2fC4b( part.TopCount, part.TopIndex ); } }
void DrawBigPart(ChunkInfo info, ref ChunkPartInfo part) { gfx.BindVb(part.VbId); bool drawLeft = info.DrawLeft && part.LeftCount > 0; bool drawRight = info.DrawRight && part.RightCount > 0; bool drawBottom = info.DrawBottom && part.BottomCount > 0; bool drawTop = info.DrawTop && part.TopCount > 0; bool drawFront = info.DrawFront && part.FrontCount > 0; bool drawBack = info.DrawBack && part.BackCount > 0; if (drawLeft && drawRight) { gfx.FaceCulling = true; gfx.DrawIndexedVb_TrisT2fC4b(part.LeftCount + part.RightCount, part.LeftIndex); gfx.FaceCulling = false; game.Vertices += part.LeftCount + part.RightCount; } else if (drawLeft) { gfx.DrawIndexedVb_TrisT2fC4b(part.LeftCount, part.LeftIndex); game.Vertices += part.LeftCount; } else if (drawRight) { gfx.DrawIndexedVb_TrisT2fC4b(part.RightCount, part.RightIndex); game.Vertices += part.RightCount; } if (drawFront && drawBack) { gfx.FaceCulling = true; gfx.DrawIndexedVb_TrisT2fC4b(part.FrontCount + part.BackCount, part.FrontIndex); gfx.FaceCulling = false; game.Vertices += part.FrontCount + part.BackCount; } else if (drawFront) { gfx.DrawIndexedVb_TrisT2fC4b(part.FrontCount, part.FrontIndex); game.Vertices += part.FrontCount; } else if (drawBack) { gfx.DrawIndexedVb_TrisT2fC4b(part.BackCount, part.BackIndex); game.Vertices += part.BackCount; } // Special handling for top and bottom as these can go over 65536 vertices and we need to adjust the indices in this case. if (drawBottom && drawTop) { gfx.FaceCulling = true; if (part.IndicesCount > maxIndices) { int part1Count = maxIndices - part.BottomIndex; gfx.DrawIndexedVb_TrisT2fC4b(part1Count, part.BottomIndex); gfx.DrawIndexedVb_TrisT2fC4b(part.BottomCount + part.TopCount - part1Count, maxVertex, 0); } else { gfx.DrawIndexedVb_TrisT2fC4b(part.BottomCount + part.TopCount, part.BottomIndex); } gfx.FaceCulling = false; game.Vertices += part.TopCount + part.BottomCount; } else if (drawBottom) { int part1Count; if (part.IndicesCount > maxIndices && (part1Count = maxIndices - part.BottomIndex) < part.BottomCount) { gfx.DrawIndexedVb_TrisT2fC4b(part1Count, part.BottomIndex); gfx.DrawIndexedVb_TrisT2fC4b(part.BottomCount - part1Count, maxVertex, 0); } else { gfx.DrawIndexedVb_TrisT2fC4b(part.BottomCount, part.BottomIndex); } game.Vertices += part.BottomCount; } else if (drawTop) { int part1Count; if (part.IndicesCount > maxIndices && (part1Count = maxIndices - part.TopIndex) < part.TopCount) { gfx.DrawIndexedVb_TrisT2fC4b(part1Count, part.TopIndex); gfx.DrawIndexedVb_TrisT2fC4b(part.TopCount - part1Count, maxVertex, 0); } else { gfx.DrawIndexedVb_TrisT2fC4b(part.TopCount, part.TopIndex); } game.Vertices += part.TopCount; } }
void BuildChunk( ChunkInfo info, ref int chunkUpdates ) { game.ChunkUpdates++; builder.GetDrawInfo( info.CentreX - 8, info.CentreY - 8, info.CentreZ - 8, ref info.NormalParts, ref info.TranslucentParts ); if( info.NormalParts == null && info.TranslucentParts == null ) { info.Empty = true; } else { IncrementUsed( info.NormalParts ); IncrementUsed( info.TranslucentParts ); } chunkUpdates++; }
void DeleteChunk( ChunkInfo info, bool decUsed ) { info.Empty = false; #if OCCLUSION info.OcclusionFlags = 0; info.OccludedFlags = 0; #endif DeleteData( ref info.NormalParts, decUsed ); DeleteData( ref info.TranslucentParts, decUsed ); }
void DrawBigPart( ChunkInfo info, ref ChunkPartInfo part ) { api.BindVb( part.VbId ); bool drawLeft = info.DrawLeft && part.LeftCount > 0; bool drawRight = info.DrawRight && part.RightCount > 0; bool drawBottom = info.DrawBottom && part.BottomCount > 0; bool drawTop = info.DrawTop && part.TopCount > 0; bool drawFront = info.DrawFront && part.FrontCount > 0; bool drawBack = info.DrawBack && part.BackCount > 0; if( drawLeft && drawRight ) { api.FaceCulling = true; api.DrawIndexedVb_TrisT2fC4b( part.LeftCount + part.RightCount, part.LeftIndex ); api.FaceCulling = false; } else if( drawLeft ) { api.DrawIndexedVb_TrisT2fC4b( part.LeftCount, part.LeftIndex ); } else if( drawRight ) { api.DrawIndexedVb_TrisT2fC4b( part.RightCount, part.RightIndex ); } if( drawFront && drawBack ) { api.FaceCulling = true; api.DrawIndexedVb_TrisT2fC4b( part.FrontCount + part.BackCount, part.FrontIndex ); api.FaceCulling = false; } else if( drawFront ) { api.DrawIndexedVb_TrisT2fC4b( part.FrontCount, part.FrontIndex ); } else if( drawBack ) { api.DrawIndexedVb_TrisT2fC4b( part.BackCount, part.BackIndex ); } // Special handling for top and bottom as these can go over 65536 vertices and we need to adjust the indices in this case. if( drawBottom && drawTop ) { api.FaceCulling = true; if( part.IndicesCount > maxIndices ) { int part1Count = maxIndices - part.BottomIndex; api.DrawIndexedVb_TrisT2fC4b( part1Count, part.BottomIndex ); api.DrawIndexedVb_TrisT2fC4b( part.BottomCount + part.TopCount - part1Count, maxVertex, 0 ); } else { api.DrawIndexedVb_TrisT2fC4b( part.BottomCount + part.TopCount, part.BottomIndex ); } api.FaceCulling = false; } else if( drawBottom ) { int part1Count; if( part.IndicesCount > maxIndices && ( part1Count = maxIndices - part.BottomIndex ) < part.BottomCount ) { api.DrawIndexedVb_TrisT2fC4b( part1Count, part.BottomIndex ); api.DrawIndexedVb_TrisT2fC4b( part.BottomCount - part1Count, maxVertex, 0 ); } else { api.DrawIndexedVb_TrisT2fC4b( part.BottomCount, part.BottomIndex ); } } else if( drawTop ) { int part1Count; if( part.IndicesCount > maxIndices && ( part1Count = maxIndices - part.TopIndex ) < part.TopCount ) { api.DrawIndexedVb_TrisT2fC4b( part1Count, part.TopIndex ); api.DrawIndexedVb_TrisT2fC4b( part.TopCount - part1Count, maxVertex, 0 ); } else { api.DrawIndexedVb_TrisT2fC4b( part.TopCount, part.TopIndex ); } } }
public void Enqueue( ChunkInfo item ) { if( Size == array.Length ) throw new InvalidOperationException( "Queue limit reached" ); array[tail] = item; tail = (tail + 1) % array.Length; Size++; }
void DrawTranslucentPart( ChunkInfo info, ref ChunkPartInfo part ) { api.BindVb( part.VbId ); bool drawLeft = (drawAllFaces || info.DrawLeft) && part.LeftCount > 0; bool drawRight = (drawAllFaces || info.DrawRight) && part.RightCount > 0; bool drawBottom = (drawAllFaces || info.DrawBottom) && part.BottomCount > 0; bool drawTop = (drawAllFaces || info.DrawTop) && part.TopCount > 0; bool drawFront = (drawAllFaces || info.DrawFront) && part.FrontCount > 0; bool drawBack = (drawAllFaces || info.DrawBack) && part.BackCount > 0; if( drawLeft && drawRight ) { api.DrawIndexedVb_TrisT2fC4b( part.LeftCount + part.RightCount, part.LeftIndex ); } else if( drawLeft ) { api.DrawIndexedVb_TrisT2fC4b( part.LeftCount, part.LeftIndex ); } else if( drawRight ) { api.DrawIndexedVb_TrisT2fC4b( part.RightCount, part.RightIndex ); } if( drawFront && drawBack ) { api.DrawIndexedVb_TrisT2fC4b( part.FrontCount + part.BackCount, part.FrontIndex ); } else if( drawFront ) { api.DrawIndexedVb_TrisT2fC4b( part.FrontCount, part.FrontIndex ); } else if( drawBack ) { api.DrawIndexedVb_TrisT2fC4b( part.BackCount, part.BackIndex ); } if( drawBottom && drawTop ) { api.DrawIndexedVb_TrisT2fC4b( part.BottomCount + part.TopCount, part.BottomIndex ); } else if( drawBottom ) { api.DrawIndexedVb_TrisT2fC4b( part.BottomCount, part.BottomIndex ); } else if( drawTop ) { api.DrawIndexedVb_TrisT2fC4b( part.TopCount, part.TopIndex ); } }