From 6c69baeb387cc8853f1c7f3f74d56787ea6a6ed7 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Fri, 25 Sep 2015 20:21:05 +1000 Subject: [PATCH] More physics (still to go: saplings) --- ClassicalSharp/Game/Game.cs | 4 +- ClassicalSharp/Map/Map.cs | 2 +- ClassicalSharp/Rendering/MapRenderer.cs | 16 +- ClassicalSharp/Singleplayer/Physics.cs | 217 ++++++++++++------- OpenTK/OpenTK.csproj | 1 + OpenTK/Platform/Minimal.cs | 264 ++++++++++++++++++++++++ 6 files changed, 417 insertions(+), 87 deletions(-) create mode 100644 OpenTK/Platform/Minimal.cs diff --git a/ClassicalSharp/Game/Game.cs b/ClassicalSharp/Game/Game.cs index 1ade0e673..fd35ffe99 100644 --- a/ClassicalSharp/Game/Game.cs +++ b/ClassicalSharp/Game/Game.cs @@ -366,9 +366,9 @@ namespace ClassicalSharp { } public void UpdateBlock( int x, int y, int z, byte block ) { - int oldHeight = Map.GetLightHeight( x, z ); + int oldHeight = Map.GetLightHeight( x, z ) + 1; Map.SetBlock( x, y, z, block ); - int newHeight = Map.GetLightHeight( x, z ); + int newHeight = Map.GetLightHeight( x, z ) + 1; MapRenderer.RedrawBlock( x, y, z, block, oldHeight, newHeight ); } diff --git a/ClassicalSharp/Map/Map.cs b/ClassicalSharp/Map/Map.cs index 656353958..09aaf1c50 100644 --- a/ClassicalSharp/Map/Map.cs +++ b/ClassicalSharp/Map/Map.cs @@ -132,7 +132,7 @@ namespace ClassicalSharp { byte block = mapData[mapIndex]; if( info.BlocksLight[block] ) { heightmap[index] = (short)( y - 1 ); - return y; + return y - 1; } mapIndex -= oneY; } diff --git a/ClassicalSharp/Rendering/MapRenderer.cs b/ClassicalSharp/Rendering/MapRenderer.cs index f737e2512..c53027725 100644 --- a/ClassicalSharp/Rendering/MapRenderer.cs +++ b/ClassicalSharp/Rendering/MapRenderer.cs @@ -149,19 +149,15 @@ namespace ClassicalSharp { } public void RedrawBlock( int x, int y, int z, byte block, int oldHeight, int newHeight ) { - int cx = x >> 4; - int cy = y >> 4; - int cz = z >> 4; + int cx = x >> 4, bX = x & 0x0F; + int cy = y >> 4, bY = y & 0x0F; + int cz = z >> 4, bZ = z & 0x0F; // 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 newLightcy = newHeight == -1 ? 0 : newHeight >> 4; - int oldLightcy = oldHeight == -1 ? 0 : oldHeight >> 4; - - ResetChunkAndBelow( cx, cy, cz, newLightcy, oldLightcy ); - int bX = x & 0x0F; // % 16 - int bY = y & 0x0F; - int bZ = z & 0x0F; + int newLightcy = newHeight < 0 ? 0 : newHeight >> 4; + int oldLightcy = oldHeight < 0 ? 0 : oldHeight >> 4; + ResetChunkAndBelow( cx, cy, cz, newLightcy, oldLightcy ); if( bX == 0 && cx > 0 ) ResetChunkAndBelow( cx - 1, cy, cz, newLightcy, oldLightcy ); if( bY == 0 && cy > 0 ) ResetChunkAndBelow( cx, cy - 1, cz, newLightcy, oldLightcy ); if( bZ == 0 && cz > 0 ) ResetChunkAndBelow( cx, cy, cz - 1, newLightcy, oldLightcy ); diff --git a/ClassicalSharp/Singleplayer/Physics.cs b/ClassicalSharp/Singleplayer/Physics.cs index 8ad5dcc1d..68b0b8bcb 100644 --- a/ClassicalSharp/Singleplayer/Physics.cs +++ b/ClassicalSharp/Singleplayer/Physics.cs @@ -9,7 +9,7 @@ namespace ClassicalSharp.Singleplayer { Game game; public Map map; BlockInfo info; - int width, length, height, oneY; + int width, length, height, oneY, volume; const uint tickMask = 0xF8000000; const uint posMask = 0x07FFFFFF; @@ -50,10 +50,15 @@ namespace ClassicalSharp.Singleplayer { return true; } + int tickCount = 0; public void Tick() { + //if( (tickCount % 5) == 0 ) { TickLava(); TickWater(); TickFalling(); + //} + tickCount++; + TickRandomBlocks(); } public void OnBlockPlaced( int x, int y, int z, byte block ) { @@ -76,8 +81,72 @@ namespace ClassicalSharp.Singleplayer { length = map.Length; height = map.Height; oneY = height * width; + volume = height * width * length; } + #region General + // TODO: tree growing + + void TickRandomBlocks() { + for( int y = 0; y < height; y += 16 ) { + for( int z = 0; z < length; z += 16 ) { + for( int x = 0; x < width; x += 16 ) { + int lo = (y * length + z) * width + x; + int hi = ((y + 15) * length + (z + 15)) * width + (x + 15 ); + HandleBlock( rnd.Next( lo, hi ) ); + HandleBlock( rnd.Next( lo, hi ) ); + HandleBlock( rnd.Next( lo, hi ) ); + } + } + } + } + + void HandleBlock( int posIndex ) { + int x = posIndex % width; + int y = posIndex / oneY; // posIndex / (width * length) + int z = (posIndex / width) % length; + byte block = map.mapData[posIndex]; + + switch( block ) { + case (byte)Block.Dirt: + if( y > map.GetLightHeight( x, z ) ) + game.UpdateBlock( x, y, z, (byte)Block.Grass ); + break; + + case (byte)Block.Grass: + if( y <= map.GetLightHeight( x, z ) ) { + game.UpdateBlock( x, y, z, (byte)Block.Dirt ); + } + break; + + case (byte)Block.Dandelion: + case (byte)Block.Rose: + case (byte)Block.BrownMushroom: + case (byte)Block.RedMushroom: + if( y <= map.GetLightHeight( x, z ) ) + game.UpdateBlock( x, y, z, (byte)Block.Air ); + break; + + case (byte)Block.Sapling: + game.UpdateBlock( x, y, z, (byte)Block.ForestGreenWool ); + break; + + case (byte)Block.Sand: + case (byte)Block.Gravel: + if( y > 0 ) PropagateFalling( posIndex, x, y, z, block, 0 ); + break; + + case (byte)Block.Lava: + HandleLava( posIndex, x, y, z ); + break; + + case (byte)Block.Water: + HandleWater( posIndex, x, y, z ); + break; + } + } + #endregion + #region Lava Queue Lava = new Queue(); @@ -94,16 +163,19 @@ namespace ClassicalSharp.Singleplayer { int x = posIndex % width; int y = posIndex / oneY; // posIndex / (width * length) int z = (posIndex / width) % length; - - if( x > 0 ) PropagateLava( posIndex - 1, x - 1, y, z ); - if( x < width - 1 ) PropagateLava( posIndex + 1, x + 1, y, z ); - if( z > 0 ) PropagateLava( posIndex - width, x, y, z - 1 ); - if( z < length - 1 ) PropagateLava( posIndex + width, x, y, z + 1 ); - if( y > 0 ) PropagateLava( posIndex - oneY, x, y - 1, z ); + HandleLava( posIndex, x, y, z ); } } } + void HandleLava( int posIndex, int x, int y, int z ) { + if( x > 0 ) PropagateLava( posIndex - 1, x - 1, y, z ); + if( x < width - 1 ) PropagateLava( posIndex + 1, x + 1, y, z ); + if( z > 0 ) PropagateLava( posIndex - width, x, y, z - 1 ); + if( z < length - 1 ) PropagateLava( posIndex + width, x, y, z + 1 ); + if( y > 0 ) PropagateLava( posIndex - oneY, x, y - 1, z ); + } + void PropagateLava( int posIndex, int x, int y, int z ) { byte block = map.mapData[posIndex]; if( block == (byte)Block.Water || block == (byte)Block.StillWater ) { @@ -132,16 +204,19 @@ namespace ClassicalSharp.Singleplayer { int x = posIndex % width; int y = posIndex / oneY; // posIndex / (width * length) int z = (posIndex / width) % length; - - if( x > 0 ) PropagateWater( posIndex - 1, x - 1, y, z ); - if( x < width - 1 ) PropagateWater( posIndex + 1, x + 1, y, z ); - if( z > 0 ) PropagateWater( posIndex - width, x, y, z - 1 ); - if( z < length - 1 ) PropagateWater( posIndex + width, x, y, z + 1 ); - if( y > 0 ) PropagateWater( posIndex - oneY, x, y - 1, z ); + HandleWater( posIndex, x, y, z ); } } } + void HandleWater( int posIndex, int x, int y, int z ) { + if( x > 0 ) PropagateWater( posIndex - 1, x - 1, y, z ); + if( x < width - 1 ) PropagateWater( posIndex + 1, x + 1, y, z ); + if( z > 0 ) PropagateWater( posIndex - width, x, y, z - 1 ); + if( z < length - 1 ) PropagateWater( posIndex + width, x, y, z + 1 ); + if( y > 0 ) PropagateWater( posIndex - oneY, x, y - 1, z ); + } + void PropagateWater( int posIndex, int x, int y, int z ) { byte block = map.mapData[posIndex]; if( block == (byte)Block.Lava || block == (byte)Block.StillLava ) { @@ -154,6 +229,52 @@ namespace ClassicalSharp.Singleplayer { #endregion + + #region Sand/Gravel + + Queue Falling = new Queue(); + const uint defFallingTick = 2u << tickShift; + + void TickFalling() { + int count = Falling.Count; + for( int i = 0; i < count; i++ ) { + int posIndex, flags; + if( CheckItem( Falling, 0x2, out posIndex, out flags ) ) { + byte block = map.mapData[posIndex]; + if( !(block == (byte)Block.Sand || block == (byte)Block.Gravel ) ) continue; + + int x = posIndex % width; + int y = posIndex / oneY; // posIndex / (width * length) + int z = (posIndex / width) % length; + if( y > 0 ) PropagateFalling( posIndex, x, y, z, block, flags ); + } + } + } + + void PropagateFalling( int posIndex, int x, int y, int z, byte block, int flags ) { + byte newBlock = map.mapData[posIndex - oneY]; + if( newBlock == 0 || info.IsLiquid[newBlock] ) { + uint newFlags = MakeFallingFlags( newBlock ) << tickShift; + Falling.Enqueue( newFlags | (uint)(posIndex - oneY) ); + + game.UpdateBlock( x, y, z, oldBlock[flags >> 2] ); + game.UpdateBlock( x, y - 1, z, block ); + } + } + + static byte[] oldBlock = new byte[] { (byte)Block.Air, (byte)Block.StillWater, + (byte)Block.Water, (byte)Block.StillLava, (Byte)Block.Lava }; + static uint MakeFallingFlags( byte above ) { + byte flags = 2; + if( above == (byte)Block.StillWater ) flags |= 0x04; // 1 + else if( above == (byte)Block.Water ) flags |= 0x08; // 2 + else if( above == (byte)Block.StillLava ) flags |= 0x0C; // 3 + else if( above == (byte)Block.Lava ) flags |= 0x10; // 4 + return flags; + } + + #endregion + #region TNT Vector3[] rayDirs; @@ -188,21 +309,21 @@ namespace ClassicalSharp.Singleplayer { } void InitExplosionCache() { - hardness = new float[] { 0, 30, 3, 2.5f, 30, 15, 0, 1.8E+07f, 500, 500, 500, 500, 2.5f, - 3, 15, 15, 15, 10, 1, 3, 1.5f, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, + hardness = new float[] { 0, 30, 3, 2.5f, 30, 15, 0, 1.8E+07f, 500, 500, 500, 500, 2.5f, + 3, 15, 15, 15, 10, 1, 3, 1.5f, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 30, 30, 30, 30, 30, 0, 7.5f, 30, 6000, 30, 0, 4, 0.5f, 0, 4, 4, 4, 4, 4, 2.5f, // Note that the 30, 500, 15, 15 are guesses (CeramicTile --> Crate) - 30, 500, 15, 15, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 30, 500, 15, 15, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; rayDirs = new Vector3[1352]; int index = 0; - // Y bottom and top planes + // Y bottom and top planes for( float x = -1; x <= 1.001f; x += 2f/15) { for( float z = -1; z <= 1.001f; z += 2f/15) { rayDirs[index++] = Vector3.Normalize( x, -1, z ); @@ -226,57 +347,5 @@ namespace ClassicalSharp.Singleplayer { } #endregion - - #region Sapling - // TODO: tree growing - #endregion - - #region Grass - // TODO: grass growing - #endregion - - #region Sand/Gravel - - Queue Falling = new Queue(); - const uint defFallingTick = 2u << tickShift; - - void TickFalling() { - int count = Falling.Count; - for( int i = 0; i < count; i++ ) { - int posIndex, flags; - if( CheckItem( Falling, 0x2, out posIndex, out flags ) ) { - byte block = map.mapData[posIndex]; - if( !(block == (byte)Block.Sand || block == (byte)Block.Gravel ) ) continue; - - int x = posIndex % width; - int y = posIndex / oneY; // posIndex / (width * length) - int z = (posIndex / width) % length; - if( y > 0 ) PropagateFalling( posIndex - oneY, x, y - 1, z, block, flags ); - } - } - } - - void PropagateFalling( int posIndex, int x, int y, int z, byte block, int flags ) { - byte newBlock = map.mapData[posIndex]; - if( newBlock == 0 || info.IsLiquid[newBlock] ) { - uint newFlags = MakeFallingFlags( newBlock ) << tickShift; - Falling.Enqueue( newFlags | (uint)posIndex ); - game.UpdateBlock( x, y, z, oldBlock[flags >> 2] ); - game.UpdateBlock( x, y - 1, z, block ); - } - } - - static byte[] oldBlock = new byte[] { (byte)Block.Air, (byte)Block.StillWater, - (byte)Block.Water, (byte)Block.StillLava, (Byte)Block.Lava }; - static uint MakeFallingFlags( byte above ) { - byte flags = 2; - if( above == (byte)Block.StillWater ) flags |= 0x04; // 1 - else if( above == (byte)Block.Water ) flags |= 0x08; // 2 - else if( above == (byte)Block.StillLava ) flags |= 0x0C; // 3 - else if( above == (byte)Block.Lava ) flags |= 0x10; // 4 - return flags; - } - - #endregion } } \ No newline at end of file diff --git a/OpenTK/OpenTK.csproj b/OpenTK/OpenTK.csproj index 4ecdcc580..11417eac4 100644 --- a/OpenTK/OpenTK.csproj +++ b/OpenTK/OpenTK.csproj @@ -98,6 +98,7 @@ + diff --git a/OpenTK/Platform/Minimal.cs b/OpenTK/Platform/Minimal.cs new file mode 100644 index 000000000..cb6262117 --- /dev/null +++ b/OpenTK/Platform/Minimal.cs @@ -0,0 +1,264 @@ +#if OPENTK_MINIMAL +using System; +// These classes fill in for missing Rectangle, Point, and Size implementations + +namespace System.Drawing { + + public struct Rectangle { + + public static readonly Rectangle Empty = default(Rectangle); + public int X, Y; + public int Width, Height; + + public Point Location { + get { return new Point( X, Y ); } + set { X = value.X; Y = value.Y; } + } + + public Size Size { + get { return new Size( Width, Height ); } + set { Width = value.Width; Height = value.Height; } + } + + public int Left { + get { return X; } + } + + public int Top { + get { return Y; } + } + + public int Right { + get { return X + Width; } + } + + public int Bottom { + get { return Y + Height; } + } + + public bool IsEmpty { + get { return Height == 0 && Width == 0 && X == 0 && Y == 0; } + } + + public Rectangle( int x, int y, int width, int height ) { + X = x; Y = y; + Width = width; Height = height; + } + + public Rectangle( Point loc, Size size ) { + X = loc.X; Y = loc.Y; + Width = size.Width; Height = size.Height; + } + + public static Rectangle FromLTRB( int left, int top, int right, int bottom ) { + return new Rectangle( left, top, right - left, bottom - top ); + } + + public override bool Equals( object obj ) { + return (obj is Rectangle) && Equals( (Rectangle)obj ); + } + + public bool Equals( Rectangle other ) { + return X == other.X && Y == other.Y && Width == other.Width && Height == other.Height; + } + + public override int GetHashCode() { + return 1000000007 * X + 1000000009 * Y + + 1000000021 * Width + 1000000033 * Height; + } + + public static bool operator == ( Rectangle lhs, Rectangle rhs ) { + return lhs.Equals( rhs ); + } + + public static bool operator != ( Rectangle lhs, Rectangle rhs ) { + return !(lhs == rhs); + } + + public bool Contains(int x, int y) { + return X <= x && x < X + Width && Y <= y && y < Y + Height; + } + + public bool Contains( Point pt ) { + return Contains( pt.X, pt.Y ); + } + + public bool Contains( Rectangle rect ) { + return X <= rect.X && rect.X + rect.Width <= X + Width && Y <= rect.Y && rect.Y + rect.Height <= Y + Height; + } + + public override string ToString() { + return X + ", " + Y + " : " + Width + "," + Height; + } + } + + public struct Size { + + public static readonly Size Empty = default(Size); + public int Width, Height; + + public bool IsEmpty { + get { return Width == 0 && Height == 0; } + } + + public Size( Point point ) { + Width = point.X; Height = point.Y; + } + + public Size( int width, int height ) { + Width = width; Height = height; + } + + public static Size Ceiling( SizeF value ) { + return new Size( (int)Math.Ceiling( value.Width ), (int)Math.Ceiling( value.Height ) ); + } + + public override bool Equals( object obj ) { + return (obj is Size) && Equals( (Size)obj ); + } + + public bool Equals( Size other ) { + return Width == other.Width && Height == other.Height; + } + + public override int GetHashCode() { + return 1000000007 * Width + 1000000009 * Height; + } + + public static bool operator == ( Size lhs, Size rhs ) { + return lhs.Width == rhs.Width && lhs.Height == rhs.Height; + } + + public static bool operator != ( Size lhs, Size rhs ) { + return !(lhs == rhs); + } + + public override string ToString() { + return Width + "," + Height; + } + } + + public struct SizeF { + + public static readonly SizeF Empty = default(SizeF); + public float Width, Height; + + public bool IsEmpty { + get { return Width == 0 && Height == 0; } + } + + public SizeF( float width, float height ) { + Width = width; Height = height; + } + + public override bool Equals( object obj ) { + return (obj is SizeF) && Equals( (SizeF)obj ); + } + + public bool Equals( SizeF other ) { + return Width == other.Width && Height == other.Height; + } + + public override int GetHashCode() { + return 1000000007 * Width.GetHashCode() + + 1000000009 * Height.GetHashCode(); + } + + public static bool operator == ( SizeF lhs, SizeF rhs ) { + return lhs.Width == rhs.Width && lhs.Height == rhs.Height; + } + + public static bool operator != ( SizeF lhs, SizeF rhs ) { + return !(lhs == rhs); + } + + public override string ToString() { + return Width + "," + Height; + } + } + + public struct Point { + + public static readonly Point Empty = default(Point); + public int X, Y; + + public bool IsEmpty { + get { return X == 0 && Y == 0; } + } + + public Point( Size size ) { + X = size.Width; Y = size.Height; + } + + public Point( int x, int y ) { + X = x; Y = y; + } + + public override bool Equals( object obj ) { + return (obj is Point) && Equals( (Point)obj ); + } + + public bool Equals( Point other ) { + return X == other.X && Y == other.Y; + } + + public override int GetHashCode() { + return 1000000007 * X + 1000000009 * Y; + } + + public static bool operator == ( Point lhs, Point rhs ) { + return lhs.X == rhs.X && lhs.Y == rhs.Y; + } + + public static bool operator != ( Point lhs, Point rhs ) { + return !(lhs == rhs); + } + + public override string ToString() { + return X + "," + Y; + } + } + + public struct Color { + public byte R, G, B, A; + + public Color( int r, int g, int b, int a ) { + A = (byte)a; + R = (byte)r; + G = (byte)g; + B = (byte)b; + } + + public Color( int r, int g, int b ) { + A = 255; + R = (byte)r; + G = (byte)g; + B = (byte)b; + } + + public override bool Equals( object obj ) { + return ( obj is Color ) && Equals( (Color)obj ); + } + + public bool Equals( Color other ) { + return A == other.A && R == other.R && G == other.G && B == other.B; + } + + public override int GetHashCode() { + return A << 24 | R << 16 | G << 8 | B; + } + + public static bool operator == ( Color left, Color right ) { + return left.Equals( right ); + } + + public static bool operator != ( Color left, Color right ) { + return !left.Equals( right ); + } + + public override string ToString() { + return R + ", " + G + ", " + B + " : " + A; + } + } +} +#endif \ No newline at end of file