diff --git a/ClassicalSharp/ClassicalSharp.csproj b/ClassicalSharp/ClassicalSharp.csproj index f795bf055..3e491747c 100644 --- a/ClassicalSharp/ClassicalSharp.csproj +++ b/ClassicalSharp/ClassicalSharp.csproj @@ -196,6 +196,7 @@ + diff --git a/ClassicalSharp/Game/Game.cs b/ClassicalSharp/Game/Game.cs index fd9e445b1..c621444d3 100644 --- a/ClassicalSharp/Game/Game.cs +++ b/ClassicalSharp/Game/Game.cs @@ -56,6 +56,7 @@ namespace ClassicalSharp { public Events Events = new Events(); public InputHandler InputHandler; public ChatLog Chat; + public BlockHandRenderer BlockHandRenderer; public IPAddress IPAddress; public string Username; @@ -172,6 +173,8 @@ namespace ClassicalSharp { ParticleManager = new ParticleManager( this ); WeatherRenderer = new WeatherRenderer( this ); WeatherRenderer.Init(); + BlockHandRenderer = new BlockHandRenderer( this ); + BlockHandRenderer.Init(); bool vsync = Options.GetBool( OptionsKey.VSync, true ); Graphics.SetVSync( this, vsync ); @@ -257,10 +260,12 @@ namespace ClassicalSharp { bool middle = IsMousePressed( MouseButton.Middle ); bool right = IsMousePressed( MouseButton.Right ); InputHandler.PickBlocks( true, left, middle, right ); + BlockHandRenderer.Render( e.Time ); } else { SelectedPos.SetAsInvalid(); } + Graphics.Mode2D( Width, Height, EnvRenderer is StandardEnvRenderer ); fpsScreen.Render( e.Time ); if( activeScreen == null || !activeScreen.HidesHud ) diff --git a/ClassicalSharp/Model/BlockModel.cs b/ClassicalSharp/Model/BlockModel.cs index 8c8649eba..6dd765633 100644 --- a/ClassicalSharp/Model/BlockModel.cs +++ b/ClassicalSharp/Model/BlockModel.cs @@ -1,5 +1,6 @@ using System; using ClassicalSharp.GraphicsAPI; +using ClassicalSharp.Renderers; using OpenTK; namespace ClassicalSharp.Model { @@ -25,11 +26,18 @@ namespace ClassicalSharp.Model { } public override BoundingBox PickingBounds { - get { return new BoundingBox( -0.5f, 0f, -0.5f, 0.5f, blockHeight, 0.5f ); } + get { return new BoundingBox( -scale, 0f, -scale, scale, blockHeight, scale ); } } protected override void DrawPlayerModel( Player p ) { - block = Byte.Parse( p.ModelName ); + // TODO: using 'is' is ugly, but means we can avoid creating + // a string every single time held block changes. + if( p is FakePlayer ) { + col = FastColour.Scale( FastColour.White, 0.8f ); + block = ((FakePlayer)p).Block; + } else { + block = Byte.Parse( p.ModelName ); + } if( block == 0 ) { blockHeight = 1; return; @@ -41,21 +49,28 @@ namespace ClassicalSharp.Model { BlockInfo = game.BlockInfo; if( BlockInfo.IsSprite[block] ) { - XQuad( 0f, TileSide.Right, false ); - ZQuad( 0f, TileSide.Back, false ); + float offset = TerrainAtlas2D.invElementSize / 2; + XQuad( 0f, TileSide.Right, -scale, 0, 0, -offset, false ); + ZQuad( 0f, TileSide.Back, 0, scale, offset, 0, false ); + + XQuad( 0f, TileSide.Right, 0, scale, offset, 0, false ); + ZQuad( 0f, TileSide.Back, -scale, 0, 0, -offset, false ); } else { - YQuad( blockHeight, TileSide.Top ); - XQuad( -0.5f, TileSide.Right, false ); - XQuad( 0.5f, TileSide.Left, true ); - ZQuad( -0.5f, TileSide.Front, true ); - ZQuad( 0.5f, TileSide.Back, false ); YQuad( 0f, TileSide.Bottom ); + XQuad( scale, TileSide.Left, -scale, scale, 0, 0, true ); + ZQuad( -scale, TileSide.Front, -scale, scale, 0, 0, true ); + + ZQuad( scale, TileSide.Back, -scale, scale, 0, 0, true ); + YQuad( blockHeight, TileSide.Top ); + XQuad( -scale, TileSide.Right, -scale, scale, 0, 0, true ); + } graphics.UpdateDynamicIndexedVb( DrawMode.Triangles, cache.vb, cache.vertices, index, index * 6 / 4 ); } float blockHeight; TerrainAtlas2D atlas; BlockInfo BlockInfo; + float scale = 0.5f; public override void Dispose() { } @@ -64,36 +79,45 @@ namespace ClassicalSharp.Model { int texId = BlockInfo.GetTextureLoc( block, side ); TextureRec rec = atlas.GetAdjTexRec( texId ); - cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + -0.5f, pos.Y + y, pos.Z + -0.5f, rec.U1, rec.V1, col ); - cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + 0.5f, pos.Y + y, pos.Z + -0.5f, rec.U2, rec.V1, col ); - cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + 0.5f, pos.Y + y, pos.Z + 0.5f, rec.U2, rec.V2, col ); - cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + -0.5f, pos.Y + y, pos.Z + 0.5f, rec.U1, rec.V2, col ); + cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X - scale, pos.Y + y, pos.Z - scale, rec.U1, rec.V1, col ); + cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + scale, pos.Y + y, pos.Z - scale, rec.U2, rec.V1, col ); + cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + scale, pos.Y + y, pos.Z + scale, rec.U2, rec.V2, col ); + cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X - scale, pos.Y + y, pos.Z + scale, rec.U1, rec.V2, col ); } - void ZQuad( float z, int side, bool swapU ) { + void ZQuad( float z, int side, float x1, float x2, + float u1Offset, float u2Offset, bool swapU ) { int texId = BlockInfo.GetTextureLoc( block, side ); TextureRec rec = atlas.GetAdjTexRec( texId ); if( blockHeight != 1 ) rec.V2 = rec.V1 + blockHeight * TerrainAtlas2D.invElementSize * (15.99f/16f); - if( swapU ) rec.SwapU(); - cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + -0.5f, pos.Y + 0f, pos.Z + z, rec.U1, rec.V2, col ); - cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + -0.5f, pos.Y + blockHeight, pos.Z + z, rec.U1, rec.V1, col ); - cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + 0.5f, pos.Y + blockHeight, pos.Z + z, rec.U2, rec.V1, col ); - cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + 0.5f, pos.Y + 0f, pos.Z + z, rec.U2, rec.V2, col ); + // need to break into two quads when drawing a sprite model in hand. + if( swapU ) rec.SwapU(); + rec.U1 += u1Offset; + rec.U2 += u2Offset; + + cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + x1, pos.Y + 0f, pos.Z + z, rec.U1, rec.V2, col ); + cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + x1, pos.Y + blockHeight, pos.Z + z, rec.U1, rec.V1, col ); + cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + x2, pos.Y + blockHeight, pos.Z + z, rec.U2, rec.V1, col ); + cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + x2, pos.Y + 0f, pos.Z + z, rec.U2, rec.V2, col ); } - void XQuad( float x, int side, bool swapU ) { + void XQuad( float x, int side, float z1, float z2, + float u1Offset, float u2Offset, bool swapU ) { int texId = BlockInfo.GetTextureLoc( block, side ); TextureRec rec = atlas.GetAdjTexRec( texId ); if( blockHeight != 1 ) rec.V2 = rec.V1 + blockHeight * TerrainAtlas2D.invElementSize * (15.99f/16f); - if( swapU ) rec.SwapU(); - cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + x, pos.Y + 0f, pos.Z + -0.5f, rec.U1, rec.V2, col ); - cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + x, pos.Y + blockHeight, pos.Z + -0.5f, rec.U1, rec.V1, col ); - cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + x, pos.Y + blockHeight, pos.Z + 0.5f, rec.U2, rec.V1, col ); - cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + x, pos.Y + 0f, pos.Z + 0.5f, rec.U2, rec.V2, col ); + if( swapU ) rec.SwapU(); + rec.U1 += u1Offset; + rec.U2 += u2Offset; + + cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + x, pos.Y + 0f, pos.Z + z1, rec.U1, rec.V2, col ); + cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + x, pos.Y + blockHeight, pos.Z + z1, rec.U1, rec.V1, col ); + cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + x, pos.Y + blockHeight, pos.Z + z2, rec.U2, rec.V1, col ); + cache.vertices[index++] = new VertexPos3fTex2fCol4b( pos.X + x, pos.Y + 0f, pos.Z + z2, rec.U2, rec.V2, col ); } } } \ No newline at end of file diff --git a/ClassicalSharp/Rendering/BlockHandRenderer.cs b/ClassicalSharp/Rendering/BlockHandRenderer.cs new file mode 100644 index 000000000..018f19772 --- /dev/null +++ b/ClassicalSharp/Rendering/BlockHandRenderer.cs @@ -0,0 +1,71 @@ +using System; +using ClassicalSharp.Model; +using OpenTK; + +namespace ClassicalSharp.Renderers { + + public class BlockHandRenderer : IDisposable { + + Game game; + BlockModel block; + FakePlayer fakePlayer; + + public BlockHandRenderer( Game window ) { + this.game = window; + } + + public void Init() { + block = new BlockModel( game ); + fakePlayer = new FakePlayer( game ); + SetupMatrices(); + } + + public void Render( double delta ) { + game.Graphics.Texturing = true; + game.Graphics.DepthTest = false; + game.Graphics.AlphaTest = true; + + byte type = (byte)game.Inventory.HeldBlock; + if( game.BlockInfo.IsSprite[type] ) + game.Graphics.LoadMatrix( ref spriteMat ); + else + game.Graphics.LoadMatrix( ref normalMat ); + fakePlayer.Block = type; + block.RenderModel( fakePlayer ); + + game.Graphics.LoadMatrix( ref game.View ); + game.Graphics.Texturing = false; + game.Graphics.DepthTest = true; + game.Graphics.AlphaTest = false; + } + + + Matrix4 normalMat, spriteMat; + void SetupMatrices() { + Matrix4 m = Matrix4.Identity; + m = m * Matrix4.Scale( 0.6f ); + m = m * Matrix4.RotateY( 45 * Utils.Deg2Rad ); + + normalMat = m * Matrix4.Translate( 0.85f, -1.35f, -1.5f ); + spriteMat = m * Matrix4.Translate( 0.85f, -1.05f, -1.5f ); + } + + public void Dispose() { + } + } + + internal class FakePlayer : Player { + + public FakePlayer( Game game ) : base( game ) { + } + public byte Block; + + public override void SetLocation( LocationUpdate update, bool interpolate ) { } + + public override void Tick( double delta ) { } + + public override void RenderModel( double deltaTime, float t ) { } + + public override void RenderName() { } + } +} diff --git a/ClassicalSharp/Rendering/PickingRenderer.cs b/ClassicalSharp/Rendering/PickingRenderer.cs index a43f8cfa6..8944b1f49 100644 --- a/ClassicalSharp/Rendering/PickingRenderer.cs +++ b/ClassicalSharp/Rendering/PickingRenderer.cs @@ -4,7 +4,7 @@ using OpenTK; namespace ClassicalSharp.Renderers { - public class PickingRenderer { + public class PickingRenderer : IDisposable { IGraphicsApi graphics; BlockInfo info; diff --git a/readme.md b/readme.md index d7cfbcda9..697669fa9 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -ClassicalSharp is a custom Minecraft Classic client written in C# that works on both Windows, Linux and OSX. +ClassicalSharp is a custom Minecraft Classic client written in C# that works on Windows, Linux and OSX. **It is not affiliated with (or supported by) Mojang AB, Minecraft, or Microsoft in any way.** ![screenshot_n](https://cloud.githubusercontent.com/assets/6509348/10800494/288b4f00-7e06-11e5-8344-5df33625cc8b.png)