ClassiCube/ClassicalSharp/Rendering/MapRenderer.Occlusion.cs

196 lines
6.2 KiB
C#

using System;
using ClassicalSharp.GraphicsAPI;
using OpenTK;
namespace ClassicalSharp {
public partial class MapRenderer : IDisposable {
void SimpleOcclusionCulling() { // TODO: broken
Vector3 p = game.LocalPlayer.EyePosition;
Vector3I chunkLoc = Vector3I.Floor( p );
Utils.Clamp( ref chunkLoc.X, 0, game.Map.Width - 1 );
Utils.Clamp( ref chunkLoc.Y, 0, game.Map.Height - 1 );
Utils.Clamp( ref chunkLoc.Z, 0, game.Map.Length- 1 );
int cx = chunkLoc.X >> 4;
int cy = chunkLoc.Y >> 4;
int cz = chunkLoc.Z >> 4;
ChunkInfo chunkIn = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
byte chunkInFlags = chunkIn.OcclusionFlags;
chunkIn.OcclusionFlags = 0;
ChunkQueue queue = new ChunkQueue( chunksX * chunksY * chunksZ );
for( int i = 0; i < chunks.Length; i++ ) {
chunks[i].Visited = false;
chunks[i].Occluded = false;
chunks[i].VisibilityFlags = 0;
}
chunkIn.Visited = true;
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( chunkIn, queue );
chunkIn.OcclusionFlags = chunkInFlags;
}
void ProcessQueue( ChunkInfo src, ChunkQueue queue ) {
Vector3I p = new Vector3I( src.CentreX, src.CentreY, src.CentreZ );
while( queue.Size > 0 ) {
ChunkInfo chunk = queue.Dequeue();
chunk.VisibilityFlags = chunk.OcclusionFlags;
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 cx = chunk.CentreX >> 4;
int cy = chunk.CentreY >> 4;
int cz = chunk.CentreZ >> 4;
int xOffset, yOffset, zOffset;
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; xOffset = -1;
} else {
distX = dxRight * dxRight + dy * dy + dz * dz; xOffset = 1;
}
// 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; zOffset = -1;
} else {
distZ = dx * dx + dy * dy + dxBack * dxBack; zOffset = 1;
}
// 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; yOffset = -1;
} else {
distY = dx * dx + dxTop * dxTop + dz * dz; yOffset = 1;
}
int distMin = Math.Min( distX, Math.Min( distY, distZ ) );
bool occlude = true;
byte flags = 0;
if( distMin == distX )
OccludeX( cx, cy, cz, xOffset, ref occlude, ref flags );
if( distMin == distZ )
OccludeZ( cx, cy, cz, zOffset, ref occlude, ref flags );
if( distMin == distY )
OccludeY( cx, cy, cz, yOffset, ref occlude, ref flags );
if( occlude )
chunk.Occluded = true;
chunk.VisibilityFlags = (byte)( flags | chunk.OcclusionFlags );
QueueChunk( cx - 1, cy, cz, queue );
QueueChunk( cx + 1, cy, cz, queue );
QueueChunk( cx, cy, cz - 1, queue );
QueueChunk( cx, cy, cz + 1, queue );
QueueChunk( cx, cy - 1, cz, queue );
QueueChunk( cx, cy + 1, cz, queue );
}
Console.WriteLine( "======================" );
}
void OccludeX( int cx, int cy, int cz, int xOffset, ref bool occlude, ref byte flags ) {
cx += xOffset;
if( cx >= 0 && cx < chunksX ) {
ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
if( (neighbour.VisibilityFlags & 1) == 0 )
occlude = false;
else
flags |= 1;
}
}
void OccludeZ( int cx, int cy, int cz, int zOffset, ref bool occlude, ref byte flags ) {
cz += zOffset;
if( cz >= 0 && cz < chunksZ ) {
ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
if( (neighbour.VisibilityFlags & 2) == 0 )
occlude = false;
else
flags |= 2;
}
}
void OccludeY( int cx, int cy, int cz, int yOffset, ref bool occlude, ref byte flags ) {
cy += yOffset;
if( cy >= 0 && cy< chunksY ) {
ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
if( (neighbour.VisibilityFlags & 4) == 0 )
occlude = false;
else
flags |= 4;
}
}
static float DistToRecSquared( Vector3 p, int x1, int y1, int z1, int x2, int y2, int z2 ) {
float dx = Math.Max( x1 - p.X, Math.Max( 0, p.X - x2 ) );
float dy = Math.Max( y1 - p.Y, Math.Max( 0, p.Y - y2 ) );
float dz = Math.Max( z1 - p.Z, Math.Max( 0, p.Z - z2 ) );
return dx * dx + dy * dy + dz * dz;
}
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;
}
}
class ChunkQueue {
ChunkInfo[] array;
int head, tail;
public int Size;
public ChunkQueue( int capacity ) {
array = new ChunkInfo[capacity];
}
public void Clear() {
Array.Clear( array, 0, Size );
head = 0;
tail = 0;
Size = 0;
}
public void Enqueue( ChunkInfo item ) {
if( Size == array.Length )
throw new InvalidOperationException( "Queue limit reached" );
array[tail] = item;
tail = (tail + 1) % array.Length;
Size++;
}
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;
}
}
}
}