From 3b3a155243a1a6927b54854a77a50dbee72d1863 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Wed, 3 Feb 2016 13:31:57 +1100 Subject: [PATCH] Optimise chunk mesh building functions even further. --- ClassicalSharp/Blocks/BlockInfo.Culling.cs | 2 +- ClassicalSharp/Commands/DefaultCommands.cs | 3 - ClassicalSharp/Map/ChunkMeshBuilder.cs | 178 +++++++++--------- .../Map/ChunkMeshBuilderTex2Col4.cs | 5 +- 4 files changed, 88 insertions(+), 100 deletions(-) diff --git a/ClassicalSharp/Blocks/BlockInfo.Culling.cs b/ClassicalSharp/Blocks/BlockInfo.Culling.cs index ce7121dc5..2323c18a3 100644 --- a/ClassicalSharp/Blocks/BlockInfo.Culling.cs +++ b/ClassicalSharp/Blocks/BlockInfo.Culling.cs @@ -6,7 +6,7 @@ namespace ClassicalSharp { /// Stores various properties about the blocks in Minecraft Classic. 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]; diff --git a/ClassicalSharp/Commands/DefaultCommands.cs b/ClassicalSharp/Commands/DefaultCommands.cs index 7b5b9a8e2..46e3041b5 100644 --- a/ClassicalSharp/Commands/DefaultCommands.cs +++ b/ClassicalSharp/Commands/DefaultCommands.cs @@ -97,9 +97,6 @@ namespace ClassicalSharp.Commands { game.Chat.Add( "map width: " + game.Map.Width ); game.Chat.Add( "map height: " + game.Map.Height ); 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 { game.Chat.Add( "&e/client info: Unrecognised property: \"&f" + property + "&e\"." ); } diff --git a/ClassicalSharp/Map/ChunkMeshBuilder.cs b/ClassicalSharp/Map/ChunkMeshBuilder.cs index 9670df3ca..eeb469d35 100644 --- a/ClassicalSharp/Map/ChunkMeshBuilder.cs +++ b/ClassicalSharp/Map/ChunkMeshBuilder.cs @@ -7,7 +7,7 @@ namespace ClassicalSharp { /// Class responsible for converting a 16x16x16 into an optimised mesh of vertices. /// This class is heavily optimised and as such may suffer from slightly unreadable code. - public partial class ChunkMeshBuilder { + public unsafe partial class ChunkMeshBuilder { int X, Y, Z; float x1, y1, z1, x2, y2, z2; @@ -29,13 +29,16 @@ namespace ClassicalSharp { internal int width, length, height, clipLevel; int maxX, maxY, maxZ; - byte[] counts = new byte[chunkSize3 * TileSide.Sides]; - byte[] chunk = new byte[extChunkSize3]; + byte* chunk, counts; bool BuildChunk( int x1, int y1, int 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; - + MemUtils.memset( (IntPtr)countsPtr, 1, 0, chunkSize3 * TileSide.Sides ); + Stretch( x1, y1, z1 ); PostStretchTiles( x1, y1, z1 ); @@ -46,10 +49,12 @@ namespace ClassicalSharp { for( int z = z1, zz = 0; z < zMax; z++, zz++ ) { 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]; - if( !info.IsAir[tile] ) - RenderTile( chunkIndex, xx, yy, zz, x, y, z ); + if( !info.IsAir[tile] ) { + int index = ((yy << 8) | (zz << 4) | xx) * TileSide.Sides; + RenderTile( chunkIndex, index, x, y, z ); + } chunkIndex++; } } @@ -57,10 +62,9 @@ namespace ClassicalSharp { return true; } - unsafe bool ReadChunkData( int x1, int y1, int z1 ) { + bool ReadChunkData( int x1, int y1, int z1 ) { bool allAir = true, allSolid = true; - fixed( byte* chunkPtr = chunk, mapPtr = map.mapData ) { - MemUtils.memset( (IntPtr)chunkPtr, 0, 0, extChunkSize3 ); + fixed( byte* mapPtr = map.mapData ) { for( int yy = -1; yy < 17; yy++ ) { int y = yy + y1; @@ -87,12 +91,12 @@ namespace ClassicalSharp { if( allAir && block != 0 ) allAir = false; if( allSolid && !info.IsOpaque[block] ) allSolid = false; - chunkPtr[chunkIndex] = block; + chunk[chunkIndex] = block; } } } - if( x1 == 0 || y1 == 0 || z1 == 0 || x1 + chunkSize >= width || + if( x1 == 0 || y1 == 0 || z1 == 0 || x1 + chunkSize >= width || y1 + chunkSize >= height || z1 + chunkSize >= length ) allSolid = false; if( !( allAir || allSolid ) ) { map.HeightmapHint( x1 - 1, z1 - 1, mapPtr ); @@ -101,11 +105,10 @@ namespace ClassicalSharp { return allAir || allSolid; } - 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 ) { - if( !BuildChunk( x, y, z ) ) - return; - + if( !BuildChunk( x, y, z ) ) return; + for( int i = 0; i < arraysCount; i++ ) { SetPartInfo( drawInfoNormal[i], i, ref normalParts ); SetPartInfo( drawInfoTranslucent[i], i, ref translucentParts ); @@ -115,9 +118,8 @@ namespace ClassicalSharp { } 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; - int index = ((yy << 8) | (zz << 4) | xx) * TileSide.Sides; if( info.IsSprite[tile] ) { fullBright = info.FullBright[tile]; @@ -143,24 +145,15 @@ namespace ClassicalSharp { isTranslucent = info.IsTranslucent[tile]; lightFlags = info.LightOffset[tile]; - if( leftCount != 0 ) - DrawLeftFace( leftCount ); - if( rightCount != 0 ) - DrawRightFace( rightCount ); - if( frontCount != 0 ) - DrawFrontFace( frontCount ); - if( backCount != 0 ) - DrawBackFace( backCount ); - if( bottomCount != 0 ) - DrawBottomFace( bottomCount ); - if( topCount != 0 ) - DrawTopFace( topCount ); + if( leftCount != 0 ) DrawLeftFace( leftCount ); + if( rightCount != 0 ) DrawRightFace( rightCount ); + if( frontCount != 0 ) DrawFrontFace( frontCount ); + if( backCount != 0 ) DrawBackFace( backCount ); + if( bottomCount != 0 ) DrawBottomFace( bottomCount ); + if( topCount != 0 ) DrawTopFace( topCount ); } - unsafe void Stretch( int x1, int y1, int z1 ) { - fixed( byte* ptr = counts ) - MemUtils.memset( (IntPtr)ptr, 1, 0, chunkSize3 * TileSide.Sides ); - + void Stretch( int x1, int y1, int z1 ) { int xMax = Math.Min( width, x1 + chunkSize ); int yMax = Math.Min( height, y1 + chunkSize ); int zMax = Math.Min( length, z1 + chunkSize ); @@ -175,6 +168,7 @@ namespace ClassicalSharp { map.SunlightZSide = map.ShadowlightZSide = col; map.SunlightYBottom = map.ShadowlightYBottom = col; #endif + bool[] hidden = game.BlockInfo.hidden; for( int y = y1, yy = 0; y < yMax; y++, yy++ ) { for( int z = z1, zz = 0; z < zMax; z++, zz++ ) { @@ -198,73 +192,69 @@ namespace ClassicalSharp { } else { X = x; Y = y; Z = z; fullBright = info.FullBright[tile]; - TestAndStretchZ( zz, countIndex, tile, chunkIndex, x, 0, TileSide.Left, -1 ); - TestAndStretchZ( zz, countIndex, tile, chunkIndex, x, maxX, TileSide.Right, 1 ); - TestAndStretchX( xx, countIndex, tile, chunkIndex, z, 0, TileSide.Front, -extChunkSize ); - TestAndStretchX( xx, countIndex, tile, chunkIndex, z, maxZ, TileSide.Back, extChunkSize ); + int tileAdj = tile * BlockInfo.BlocksCount; + // All of these function calls are inlined as they can be called tens of millions to hundreds of millions of times. + + int index = countIndex + TileSide.Left; + if( counts[index] == 0 || (x == 0 && Y < clipLevel) || + (x != 0 && hidden[(tileAdj + chunk[chunkIndex - 1]) * TileSide.Sides + TileSide.Left]) ) { + counts[index] = 0; + } else { + int count = StretchZ( zz, index, X, Y, Z, chunkIndex, tile, TileSide.Left ); + AddVertices( tile, count, TileSide.Left ); counts[index] = (byte)count; + } + + index = countIndex + TileSide.Right; + if( counts[index] == 0 || (x == maxX && Y < clipLevel) || + (x != maxX && hidden[(tileAdj + chunk[chunkIndex + 1]) * TileSide.Sides + TileSide.Right]) ) { + counts[index] = 0; + } else { + int count = StretchZ( zz, index, X, Y, Z, chunkIndex, tile, TileSide.Right ); + AddVertices( tile, count, TileSide.Right ); 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; + } - if( y > 0 ) - TestAndStretchX( xx, countIndex, tile, chunkIndex, y, 0, TileSide.Bottom, -extChunkSize2 ); - else - 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; - } else { - int count = StretchX( xx, index, X, Y, Z, chunkIndex, tile, tileSide ); - AddVertices( tile, count, tileSide ); - counts[index] = (byte)count; - } - } - } - - void TestAndStretchZ( int zz, 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; - } else { - int count = StretchZ( zz, index, X, Y, Z, chunkIndex, tile, tileSide ); - AddVertices( tile, count, tileSide ); - 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 count = 1; x++; diff --git a/ClassicalSharp/Map/ChunkMeshBuilderTex2Col4.cs b/ClassicalSharp/Map/ChunkMeshBuilderTex2Col4.cs index 7b772f70e..f4f71c8ca 100644 --- a/ClassicalSharp/Map/ChunkMeshBuilderTex2Col4.cs +++ b/ClassicalSharp/Map/ChunkMeshBuilderTex2Col4.cs @@ -4,7 +4,7 @@ using ClassicalSharp.GraphicsAPI; namespace ClassicalSharp { - public partial class ChunkMeshBuilder { + public unsafe partial class ChunkMeshBuilder { DrawInfo[] drawInfoNormal, drawInfoTranslucent; 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 ) { 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 ) ); }