Optimise chunk mesh building functions even further.

This commit is contained in:
UnknownShadow200 2016-02-03 13:31:57 +11:00
parent d6e541bc91
commit 3b3a155243
4 changed files with 88 additions and 100 deletions

View File

@ -6,7 +6,7 @@ namespace ClassicalSharp {
/// <summary> Stores various properties about the blocks in Minecraft Classic. </summary> /// <summary> Stores various properties about the blocks in Minecraft Classic. </summary>
public partial class BlockInfo { public partial class BlockInfo {
bool[] hidden = new bool[BlocksCount * BlocksCount * TileSide.Sides]; public bool[] hidden = new bool[BlocksCount * BlocksCount * TileSide.Sides];
public bool[] CanStretch = new bool[BlocksCount * TileSide.Sides]; public bool[] CanStretch = new bool[BlocksCount * TileSide.Sides];

View File

@ -97,9 +97,6 @@ namespace ClassicalSharp.Commands {
game.Chat.Add( "map width: " + game.Map.Width ); game.Chat.Add( "map width: " + game.Map.Width );
game.Chat.Add( "map height: " + game.Map.Height ); game.Chat.Add( "map height: " + game.Map.Height );
game.Chat.Add( "map length: " + game.Map.Length ); game.Chat.Add( "map length: " + game.Map.Length );
} else if( Utils.CaselessEquals( property, "spam" ) ) {
for( int i = 0; i < 1000; i++ )
game.Chat.Add( "spamsdfdfd sdfd dsfdfdf &e" + i );
} else { } else {
game.Chat.Add( "&e/client info: Unrecognised property: \"&f" + property + "&e\"." ); game.Chat.Add( "&e/client info: Unrecognised property: \"&f" + property + "&e\"." );
} }

View File

@ -7,7 +7,7 @@ namespace ClassicalSharp {
/// <summary> Class responsible for converting a 16x16x16 into an optimised mesh of vertices. </summary> /// <summary> Class responsible for converting a 16x16x16 into an optimised mesh of vertices. </summary>
/// <remarks> This class is heavily optimised and as such may suffer from slightly unreadable code. </remarks> /// <remarks> This class is heavily optimised and as such may suffer from slightly unreadable code. </remarks>
public partial class ChunkMeshBuilder { public unsafe partial class ChunkMeshBuilder {
int X, Y, Z; int X, Y, Z;
float x1, y1, z1, x2, y2, z2; float x1, y1, z1, x2, y2, z2;
@ -29,12 +29,15 @@ namespace ClassicalSharp {
internal int width, length, height, clipLevel; internal int width, length, height, clipLevel;
int maxX, maxY, maxZ; int maxX, maxY, maxZ;
byte[] counts = new byte[chunkSize3 * TileSide.Sides]; byte* chunk, counts;
byte[] chunk = new byte[extChunkSize3];
bool BuildChunk( int x1, int y1, int z1 ) { bool BuildChunk( int x1, int y1, int z1 ) {
PreStretchTiles( x1, y1, z1 ); PreStretchTiles( x1, y1, z1 );
byte* chunkPtr = stackalloc byte[extChunkSize3]; chunk = chunkPtr;
byte* countsPtr = stackalloc byte[chunkSize3 * TileSide.Sides]; counts = countsPtr;
MemUtils.memset( (IntPtr)chunkPtr, 0, 0, extChunkSize3 );
if( ReadChunkData( x1, y1, z1 ) ) return false; if( ReadChunkData( x1, y1, z1 ) ) return false;
MemUtils.memset( (IntPtr)countsPtr, 1, 0, chunkSize3 * TileSide.Sides );
Stretch( x1, y1, z1 ); Stretch( x1, y1, z1 );
PostStretchTiles( x1, y1, z1 ); PostStretchTiles( x1, y1, z1 );
@ -48,8 +51,10 @@ namespace ClassicalSharp {
int chunkIndex = (yy + 1) * extChunkSize2 + (zz + 1) * extChunkSize + (0 + 1); int chunkIndex = (yy + 1) * extChunkSize2 + (zz + 1) * extChunkSize + (0 + 1);
for( int x = x1, xx = 0; x < xMax; x++, xx++ ) { for( int x = x1, xx = 0; x < xMax; x++, xx++ ) {
tile = chunk[chunkIndex]; tile = chunk[chunkIndex];
if( !info.IsAir[tile] ) if( !info.IsAir[tile] ) {
RenderTile( chunkIndex, xx, yy, zz, x, y, z ); int index = ((yy << 8) | (zz << 4) | xx) * TileSide.Sides;
RenderTile( chunkIndex, index, x, y, z );
}
chunkIndex++; chunkIndex++;
} }
} }
@ -57,10 +62,9 @@ namespace ClassicalSharp {
return true; return true;
} }
unsafe bool ReadChunkData( int x1, int y1, int z1 ) { bool ReadChunkData( int x1, int y1, int z1 ) {
bool allAir = true, allSolid = true; bool allAir = true, allSolid = true;
fixed( byte* chunkPtr = chunk, mapPtr = map.mapData ) { fixed( byte* mapPtr = map.mapData ) {
MemUtils.memset( (IntPtr)chunkPtr, 0, 0, extChunkSize3 );
for( int yy = -1; yy < 17; yy++ ) { for( int yy = -1; yy < 17; yy++ ) {
int y = yy + y1; int y = yy + y1;
@ -87,7 +91,7 @@ namespace ClassicalSharp {
if( allAir && block != 0 ) allAir = false; if( allAir && block != 0 ) allAir = false;
if( allSolid && !info.IsOpaque[block] ) allSolid = false; if( allSolid && !info.IsOpaque[block] ) allSolid = false;
chunkPtr[chunkIndex] = block; chunk[chunkIndex] = block;
} }
} }
} }
@ -103,8 +107,7 @@ namespace ClassicalSharp {
public void GetDrawInfo( int x, int y, int z, ref ChunkPartInfo[] normalParts, public void GetDrawInfo( int x, int y, int z, ref ChunkPartInfo[] normalParts,
ref ChunkPartInfo[] translucentParts, ref byte occlusionFlags ) { ref ChunkPartInfo[] translucentParts, ref byte occlusionFlags ) {
if( !BuildChunk( x, y, z ) ) if( !BuildChunk( x, y, z ) ) return;
return;
for( int i = 0; i < arraysCount; i++ ) { for( int i = 0; i < arraysCount; i++ ) {
SetPartInfo( drawInfoNormal[i], i, ref normalParts ); SetPartInfo( drawInfoNormal[i], i, ref normalParts );
@ -115,9 +118,8 @@ namespace ClassicalSharp {
} }
Vector3 minBB, maxBB; Vector3 minBB, maxBB;
public void RenderTile( int chunkIndex, int xx, int yy, int zz, int x, int y, int z ) { public void RenderTile( int chunkIndex, int index, int x, int y, int z ) {
X = x; Y = y; Z = z; X = x; Y = y; Z = z;
int index = ((yy << 8) | (zz << 4) | xx) * TileSide.Sides;
if( info.IsSprite[tile] ) { if( info.IsSprite[tile] ) {
fullBright = info.FullBright[tile]; fullBright = info.FullBright[tile];
@ -143,24 +145,15 @@ namespace ClassicalSharp {
isTranslucent = info.IsTranslucent[tile]; isTranslucent = info.IsTranslucent[tile];
lightFlags = info.LightOffset[tile]; lightFlags = info.LightOffset[tile];
if( leftCount != 0 ) if( leftCount != 0 ) DrawLeftFace( leftCount );
DrawLeftFace( leftCount ); if( rightCount != 0 ) DrawRightFace( rightCount );
if( rightCount != 0 ) if( frontCount != 0 ) DrawFrontFace( frontCount );
DrawRightFace( rightCount ); if( backCount != 0 ) DrawBackFace( backCount );
if( frontCount != 0 ) if( bottomCount != 0 ) DrawBottomFace( bottomCount );
DrawFrontFace( frontCount ); if( topCount != 0 ) DrawTopFace( topCount );
if( backCount != 0 )
DrawBackFace( backCount );
if( bottomCount != 0 )
DrawBottomFace( bottomCount );
if( topCount != 0 )
DrawTopFace( topCount );
} }
unsafe void Stretch( int x1, int y1, int z1 ) { void Stretch( int x1, int y1, int z1 ) {
fixed( byte* ptr = counts )
MemUtils.memset( (IntPtr)ptr, 1, 0, chunkSize3 * TileSide.Sides );
int xMax = Math.Min( width, x1 + chunkSize ); int xMax = Math.Min( width, x1 + chunkSize );
int yMax = Math.Min( height, y1 + chunkSize ); int yMax = Math.Min( height, y1 + chunkSize );
int zMax = Math.Min( length, z1 + chunkSize ); int zMax = Math.Min( length, z1 + chunkSize );
@ -175,6 +168,7 @@ namespace ClassicalSharp {
map.SunlightZSide = map.ShadowlightZSide = col; map.SunlightZSide = map.ShadowlightZSide = col;
map.SunlightYBottom = map.ShadowlightYBottom = col; map.SunlightYBottom = map.ShadowlightYBottom = col;
#endif #endif
bool[] hidden = game.BlockInfo.hidden;
for( int y = y1, yy = 0; y < yMax; y++, yy++ ) { for( int y = y1, yy = 0; y < yMax; y++, yy++ ) {
for( int z = z1, zz = 0; z < zMax; z++, zz++ ) { for( int z = z1, zz = 0; z < zMax; z++, zz++ ) {
@ -198,71 +192,67 @@ namespace ClassicalSharp {
} else { } else {
X = x; Y = y; Z = z; X = x; Y = y; Z = z;
fullBright = info.FullBright[tile]; fullBright = info.FullBright[tile];
TestAndStretchZ( zz, countIndex, tile, chunkIndex, x, 0, TileSide.Left, -1 ); int tileAdj = tile * BlockInfo.BlocksCount;
TestAndStretchZ( zz, countIndex, tile, chunkIndex, x, maxX, TileSide.Right, 1 ); // All of these function calls are inlined as they can be called tens of millions to hundreds of millions of times.
TestAndStretchX( xx, countIndex, tile, chunkIndex, z, 0, TileSide.Front, -extChunkSize );
TestAndStretchX( xx, countIndex, tile, chunkIndex, z, maxZ, TileSide.Back, extChunkSize );
if( y > 0 ) int index = countIndex + TileSide.Left;
TestAndStretchX( xx, countIndex, tile, chunkIndex, y, 0, TileSide.Bottom, -extChunkSize2 ); if( counts[index] == 0 || (x == 0 && Y < clipLevel) ||
else (x != 0 && hidden[(tileAdj + chunk[chunkIndex - 1]) * TileSide.Sides + TileSide.Left]) ) {
counts[countIndex + TileSide.Bottom] = 0;
TestAndStretchX( xx, countIndex, tile, chunkIndex, y, maxY + 2, TileSide.Top, extChunkSize2 );
}
}
}
}
}
void TestAndStretchX( int xx, int index, byte tile, int chunkIndex, int value, int test, int tileSide, int offset ) {
index += tileSide;
if( counts[index] != 0 ) {
if( (value == test && Y < clipLevel) ||
(value != test && info.IsFaceHidden( tile, chunk[chunkIndex + offset], tileSide )) ) {
counts[index] = 0; counts[index] = 0;
} else { } else {
int count = StretchX( xx, index, X, Y, Z, chunkIndex, tile, tileSide ); int count = StretchZ( zz, index, X, Y, Z, chunkIndex, tile, TileSide.Left );
AddVertices( tile, count, tileSide ); AddVertices( tile, count, TileSide.Left ); counts[index] = (byte)count;
counts[index] = (byte)count;
}
}
} }
void TestAndStretchZ( int zz, int index, byte tile, int chunkIndex, int value, int test, int tileSide, int offset ) { index = countIndex + TileSide.Right;
index += tileSide; if( counts[index] == 0 || (x == maxX && Y < clipLevel) ||
if( counts[index] != 0 ) { (x != maxX && hidden[(tileAdj + chunk[chunkIndex + 1]) * TileSide.Sides + TileSide.Right]) ) {
if( (value == test && Y < clipLevel) ||
(value != test && info.IsFaceHidden( tile, chunk[chunkIndex + offset], tileSide )) ) {
counts[index] = 0; counts[index] = 0;
} else { } else {
int count = StretchZ( zz, index, X, Y, Z, chunkIndex, tile, tileSide ); int count = StretchZ( zz, index, X, Y, Z, chunkIndex, tile, TileSide.Right );
AddVertices( tile, count, tileSide ); AddVertices( tile, count, TileSide.Right ); counts[index] = (byte)count;
counts[index] = (byte)count; }
index = countIndex + TileSide.Front;
if( counts[index] == 0 || (z == 0 && Y < clipLevel) ||
(z != 0 && hidden[(tileAdj + chunk[chunkIndex - 18]) * TileSide.Sides + TileSide.Front]) ) {
counts[index] = 0;
} else {
int count = StretchX( xx, index, X, Y, Z, chunkIndex, tile, TileSide.Front );
AddVertices( tile, count, TileSide.Front ); counts[index] = (byte)count;
}
index = countIndex + TileSide.Back;
if( counts[index] == 0 || (z == maxZ && Y < clipLevel) ||
(z != maxZ && hidden[(tileAdj + chunk[chunkIndex + 18]) * TileSide.Sides + TileSide.Back]) ) {
counts[index] = 0;
} else {
int count = StretchX( xx, index, X, Y, Z, chunkIndex, tile, TileSide.Back );
AddVertices( tile, count, TileSide.Back ); counts[index] = (byte)count;
}
index = countIndex + TileSide.Bottom;
if( counts[index] == 0 || y == 0 ||
hidden[(tileAdj + chunk[chunkIndex - 324]) * TileSide.Sides + TileSide.Bottom] ) {
counts[index] = 0;
} else {
int count = StretchX( xx, index, X, Y, Z, chunkIndex, tile, TileSide.Bottom );
AddVertices( tile, count, TileSide.Bottom ); counts[index] = (byte)count;
}
index = countIndex + TileSide.Top;
if( counts[index] == 0 ||
hidden[(tileAdj + chunk[chunkIndex + 324]) * TileSide.Sides + TileSide.Top] ) {
counts[index] = 0;
} else {
int count = StretchX( xx, index, X, Y, Z, chunkIndex, tile, TileSide.Top );
AddVertices( tile, count, TileSide.Top ); counts[index] = (byte)count;
}
} }
} }
} }
byte GetNeighbour( int chunkIndex, int face ) {
switch( face ) {
case TileSide.Left:
return chunk[chunkIndex - 1]; // x - 1
case TileSide.Right:
return chunk[chunkIndex + 1]; // x + 1
case TileSide.Front:
return chunk[chunkIndex - 18]; // z - 1
case TileSide.Back:
return chunk[chunkIndex + 18]; // z + 1
case TileSide.Bottom:
return chunk[chunkIndex - 324]; // y - 1
case TileSide.Top:
return chunk[chunkIndex + 324]; // y + 1
} }
return 0;
} }
int StretchX( int xx, int countIndex, int x, int y, int z, int chunkIndex, byte tile, int face ) { int StretchX( int xx, int countIndex, int x, int y, int z, int chunkIndex, byte tile, int face ) {

View File

@ -4,7 +4,7 @@ using ClassicalSharp.GraphicsAPI;
namespace ClassicalSharp { namespace ClassicalSharp {
public partial class ChunkMeshBuilder { public unsafe partial class ChunkMeshBuilder {
DrawInfo[] drawInfoNormal, drawInfoTranslucent; DrawInfo[] drawInfoNormal, drawInfoTranslucent;
TerrainAtlas1D atlas; TerrainAtlas1D atlas;
@ -63,9 +63,10 @@ namespace ClassicalSharp {
} }
} }
int[] offsets = { -1, 1, -extChunkSize, extChunkSize, -extChunkSize2, extChunkSize2 };
bool CanStretch( byte initialTile, int chunkIndex, int x, int y, int z, int face ) { bool CanStretch( byte initialTile, int chunkIndex, int x, int y, int z, int face ) {
byte tile = chunk[chunkIndex]; byte tile = chunk[chunkIndex];
return tile == initialTile && !info.IsFaceHidden( tile, GetNeighbour( chunkIndex, face ), face ) return tile == initialTile && !info.IsFaceHidden( tile, chunk[chunkIndex + offsets[face]], face )
&& (fullBright || IsLit( X, Y, Z, face, initialTile ) == IsLit( x, y, z, face, tile ) ); && (fullBright || IsLit( X, Y, Z, face, initialTile ) == IsLit( x, y, z, face, tile ) );
} }