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.**
