diff --git a/ClassicalSharp/Generator/Noise.cs b/ClassicalSharp/Generator/Noise.cs index d55b2c767..44eb132f5 100644 --- a/ClassicalSharp/Generator/Noise.cs +++ b/ClassicalSharp/Generator/Noise.cs @@ -17,53 +17,52 @@ namespace ClassicalSharp.Generator { // make a random initial permutation based on seed, // instead of using fixed permutation table in original code. for( int i = 0; i < 256; i++ ) - p[i + 256] = p[i] = rnd.Next( 256 ); + p[i + 256] = p[i] = (byte)rnd.Next( 256 ); } // TODO: need to half this maybe? public override double Compute( double x, double y ) { - int xFloor = Utils.Floor( x ), yFloor = Utils.Floor( y ); - // Find unit rectangle that contains point + int xFloor = x >= 0 ? (int)x : (int)x - 1; + int yFloor = y >= 0 ? (int)y : (int)y - 1; int X = xFloor & 0xFF, Y = yFloor & 0xFF; - // Find relative x, y of each point in rectangle. - x -= Math.Floor( x ); y -= Math.Floor( y ); + x -= xFloor; y -= yFloor; - // Compute fade curves for each of x, y. - double u = Fade( x ), v = Fade( y ); - // Hash coordinates of the 4 rectangle corners. - int A = p[X] + Y, AA = p[A], AB = p[A + 1], - B = p[X + 1] + Y, BA = p[B], BB = p[B + 1]; - - // and add blended results from 4 corners of rectangle. - return Lerp( - v, - Lerp( u, Grad( p[AA], x, y ), - Grad( p[BA], x - 1, y ) ), - Lerp( u, Grad( p[AB], x, y - 1 ), - Grad( p[BB], x - 1, y - 1 ) ) - ); - } - - static double Fade( double t ) { - return t * t * t * (t * (t * 6 - 15) + 10); - } - - static double Lerp( double t, double a, double b ) { - return a + t * (b - a); + double u = x * x * x * (x * (x * 6 - 15) + 10); // Fade(x) + double v = y * y * y * (y * (y * 6 - 15) + 10); // Fade(y) + int A = p[X] + Y, B = p[X + 1] + Y; + + double g22 = Grad( p[p[A]], x, y ), g12 = Grad( p[p[B]], x - 1, y ); + double c1 = g22 + u * (g12 - g22); + double g21 = Grad( p[p[A + 1]], x, y - 1 ), g11 = Grad( p[p[B + 1]], x - 1, y - 1 ); + double c2 = g21 + u * (g11 - g21); + return c1 + v * (c2 - c1); } static double Grad( int hash, double x, double y ) { - // convert low 4 bits of hash code into 12 gradient directions. - int h = hash & 15; - double u = h < 8 ? x : y; - double v = h < 4 ? y : h == 12 || h == 14 ? x : 0; - return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); + switch( hash & 15 ) { + case 0: return x + y; + case 1: return -x + y; + case 2: return x - y; + case 3: return -x - y; + case 4: return x; + case 5: return -x; + case 6: return x; + case 7: return -x; + case 8: return y; + case 9: return -y; + case 10: return y; + case 11: return -y; + case 12: return x + y; + case 13: return -y; + case 14: return -x + y; + case 15: return -y; + default: return 0; + } } - int[] p = new int[512]; + byte[] p = new byte[512]; } - public sealed class OctaveNoise : Noise { readonly int octaves; diff --git a/ClassicalSharp/Generator/NotchyGenerator.cs b/ClassicalSharp/Generator/NotchyGenerator.cs index 57313b278..8cf57a929 100644 --- a/ClassicalSharp/Generator/NotchyGenerator.cs +++ b/ClassicalSharp/Generator/NotchyGenerator.cs @@ -23,7 +23,7 @@ namespace ClassicalSharp.Generator { oneY = width * length; waterLevel = height / 2; blocks = new byte[width * height * length]; - rnd = new Random( 0x5553200 ); // 0x5553201 is flatter. + rnd = new Random( 0x5553201 ); // 0x5553201 is flatter. CreateHeightmap(); CreateStrata(); @@ -263,7 +263,104 @@ namespace ClassicalSharp.Generator { } void PlantTrees() { + int numPatches = width * length / 4000; + for( int i = 0; i < numPatches; i++ ) { + int patchX = rnd.Next( width ), patchZ = rnd.Next( length ); + + for( int j = 0; j < 20; j++ ) { + int treeX = patchX, treeZ = patchZ; + for( int k = 0; k < 20; k++ ) { + treeX += rnd.Next( 6 ) - rnd.Next( 6 ); + treeZ += rnd.Next( 6 ) - rnd.Next( 6 ); + if( treeX < 0 || treeZ < 0 || treeX >= width || + treeZ >= length || rnd.NextDouble() >= 0.25 ) + continue; + + int treeY = heightmap[treeZ * width + treeX] + 1; + int treeHeight = 5 + rnd.Next( 3 ); + if( CanGrowTree( treeX, treeY, treeZ, treeHeight ) ) { + GrowTree( treeX, treeY, treeZ, treeHeight ); + } + } + } + } + } + + bool CanGrowTree( int treeX, int treeY, int treeZ, int height ) { + return true; + // check tree base + int baseHeight = height - 4; + for( int y = treeY; y < treeY + baseHeight; y++ ) + for( int z = treeZ - 1; z <= treeZ + 1; z++ ) + for( int x = treeX - 1; x <= treeX + 1; x++ ) + { + if( x < 0 || y < 0 || z < 0 || x >= width || y >= height || z >= length ) + return false; + int index = (y * length + z) * width + x; + //if( blocks[index] != 0 ) return false; + } + // and also check canopy + for( int y = treeY + baseHeight; y < treeY + height; y++ ) + for( int z = treeZ - 2; z <= treeZ + 2; z++ ) + for( int x = treeX - 2; x <= treeX + 2; x++ ) + { + if( x < 0 || y < 0 || z < 0 || x >= width || y >= height || z >= length ) + return false; + int index = (y * length + z) * width + x; + //if( blocks[index] != 0 ) return false; + } + return true; + } + + void GrowTree( int treeX, int treeY, int treeZ, int height ) { + int baseHeight = height - 4; + int index = 0; + + // leaves bottom layer + for( int y = treeY + baseHeight; y < treeY + baseHeight + 2; y++ ) + for( int zz = -2; zz <= 2; zz++ ) + for( int xx = -2; xx <= 2; xx++ ) + { + int x = xx + treeX, z = zz + treeZ; + index = (y * length + z) * width + x; + if( !Check( x, y, z ) ) continue; // TODO: get rid of these once CanGrowTree is fixed. + if( Math.Abs( xx ) == 2 && Math.Abs( zz ) == 2 ) { + if( rnd.NextDouble() >= 0.5 ) + blocks[index] = (byte)Block.Leaves; + } else { + blocks[index] = (byte)Block.Leaves; + } + } + + // leaves top layer + int bottomY = treeY + baseHeight + 2; + for( int y = treeY + baseHeight + 2; y < treeY + height; y++ ) + for( int zz = -1; zz <= 1; zz++ ) + for( int xx = -1; xx <= 1; xx++ ) + { + int x = xx + treeX, z = zz + treeZ; + index = (y * length + z) * width + x; + if( !Check( x, y, z ) ) continue; + if( xx == 0 || zz == 0 ) { + blocks[index] = (byte)Block.Leaves; + } else if( y != bottomY && rnd.NextDouble() >= 0.5 ) { + blocks[index] = (byte)Block.Leaves; + } + } + + // then place trunk + index = (treeY * length + treeZ ) * width + treeX; + for( int y = 0; y < height - 1; y++ ) { + if( !Check( treeX, treeY + y, treeZ ) ) continue; + blocks[index] = (byte)Block.Wood; + index += oneY; + } + } + + bool Check( int x, int y, int z ) { + return x >= 0 && y >= 0 && z >= 0 + && x < width && y < height && z < length; } } } \ No newline at end of file diff --git a/ClassicalSharp/Singleplayer/Physics.cs b/ClassicalSharp/Singleplayer/Physics.cs index 6565aa5a6..ad80ee08d 100644 --- a/ClassicalSharp/Singleplayer/Physics.cs +++ b/ClassicalSharp/Singleplayer/Physics.cs @@ -388,10 +388,6 @@ namespace ClassicalSharp.Singleplayer { return; } - // Base trunk - for( int yy = 0; yy < trunkH; yy++ ) - game.UpdateBlock( x, y + yy, z, (byte)Block.Wood ); - // Leaves bottom layer y += trunkH; for( int zz = -2; zz <= 2; zz++ ) { @@ -418,6 +414,11 @@ namespace ClassicalSharp.Singleplayer { } } } + + // Base trunk + y -= 2 + trunkH; + for( int yy = 0; yy < trunkH + 3; yy++ ) + game.UpdateBlock( x, y + yy, z, (byte)Block.Wood ); } bool CheckBounds( int x1, int x2, int y1, int y2, int z1, int z2 ) { diff --git a/ClassicalSharp/Singleplayer/Server.cs b/ClassicalSharp/Singleplayer/Server.cs index 8023fdc98..d581b0acc 100644 --- a/ClassicalSharp/Singleplayer/Server.cs +++ b/ClassicalSharp/Singleplayer/Server.cs @@ -1,4 +1,5 @@ -using System; +//#define TEST_VANILLA +using System; using System.Net; using OpenTK; using OpenTK.Input; @@ -27,8 +28,11 @@ namespace ClassicalSharp.Singleplayer { game.Events.RaiseBlockPermissionsChanged(); NewMap(); - //MakeMap( 128, 128, 128 ); - MakeMap( 128, 64, 128 ); + #if TEST_VANILLA + MakeMap( 384, 64, 384 ); + #else + MakeMap( 128, 128, 128 ); + #endif game.CommandManager.RegisterCommand( new GenerateCommand() ); } @@ -69,14 +73,17 @@ namespace ClassicalSharp.Singleplayer { } internal unsafe void MakeMap( int width, int height, int length ) { - //byte[] map = new byte[width * height * length]; - //var sw = System.Diagnostics.Stopwatch.StartNew(); - //fixed( byte* ptr = map ) { - // MapSet( width, length, ptr, 0, height / 2 - 2, (byte)Block.Dirt ); - // MapSet( width, length, ptr, height / 2 - 1, height / 2 - 1, (byte)Block.Grass ); - //} + #if TEST_VANILLA byte[] map = new ClassicalSharp.Generator.NotchyGenerator() .GenerateMap( width, height, length ); + #else + byte[] map = new byte[width * height * length]; + fixed( byte* ptr = map ) { + MapSet( width, length, ptr, 0, height / 2 - 2, (byte)Block.Dirt ); + MapSet( width, length, ptr, height / 2 - 1, height / 2 - 1, (byte)Block.Grass ); + } + #endif + game.Map.SetData( map, width, height, length ); game.Events.RaiseOnNewMapLoaded(); game.SetNewScreen( null ); diff --git a/ClassicalSharp/Utils/Utils.cs b/ClassicalSharp/Utils/Utils.cs index 4e4750b43..dc43f1d54 100644 --- a/ClassicalSharp/Utils/Utils.cs +++ b/ClassicalSharp/Utils/Utils.cs @@ -242,10 +242,6 @@ namespace ClassicalSharp { return value >= 0 ? (int)value : (int)value - 1; } - public static int Floor( double value ) { - return value >= 0 ? (int)value : (int)value - 1; - } - public static int AdjViewDist( int value ) { return (int)(1.4142135 * value); }