diff --git a/TrueCraft.Client/ChunkConverter.cs b/TrueCraft.Client/ChunkConverter.cs index 8ef3cda..3ed656f 100644 --- a/TrueCraft.Client/ChunkConverter.cs +++ b/TrueCraft.Client/ChunkConverter.cs @@ -99,15 +99,19 @@ namespace TrueCraft.Client Coordinates3D.West }; + private readonly List OpaqueVerticies = new List(); + private readonly List OpaqueIndicies = new List(); + private readonly List TransparentVerticies = new List(); + private readonly List TransparentIndicies = new List(); + private readonly HashSet DrawableCoordinates = new HashSet(); + private Tuple ProcessChunk(ReadOnlyChunk chunk) { - var opaqueVerticies = new List(); - var opaqueIndicies = new List(); - - var transparentVerticies = new List(); - var transparentIndicies = new List(); - - var drawableCoordinates = new HashSet(); + OpaqueVerticies.Clear(); + OpaqueIndicies.Clear(); + TransparentVerticies.Clear(); + TransparentIndicies.Clear(); + DrawableCoordinates.Clear(); var boundingBox = new Microsoft.Xna.Framework.BoundingBox( new Vector3(chunk.X * Chunk.Width, 0, chunk.Z * Chunk.Depth), @@ -123,7 +127,7 @@ namespace TrueCraft.Client var id = chunk.GetBlockId(coords); var provider = BlockRepository.GetBlockProvider(id); if (id != 0) - drawableCoordinates.Add(coords); + DrawableCoordinates.Add(coords); if (!provider.Opaque) { // Add adjacent blocks @@ -137,14 +141,17 @@ namespace TrueCraft.Client continue; } if (chunk.GetBlockId(next) != 0) - drawableCoordinates.Add(next); + DrawableCoordinates.Add(next); } } } } } - foreach (var coords in drawableCoordinates) + var enumerator = DrawableCoordinates.GetEnumerator(); + for (int j = 0; j < DrawableCoordinates.Count; j++) { + var coords = enumerator.Current; + enumerator.MoveNext(); var descriptor = new BlockDescriptor { ID = chunk.GetBlockId(coords), @@ -159,23 +166,23 @@ namespace TrueCraft.Client int[] i; var v = BlockRenderer.RenderBlock(provider, descriptor, new Vector3(chunk.X * Chunk.Width + coords.X, coords.Y, chunk.Z * Chunk.Depth + coords.Z), - opaqueVerticies.Count, out i); - opaqueVerticies.AddRange(v); - opaqueIndicies.AddRange(i); + OpaqueVerticies.Count, out i); + OpaqueVerticies.AddRange(v); + OpaqueIndicies.AddRange(i); } else { int[] i; var v = BlockRenderer.RenderBlock(provider, descriptor, new Vector3(chunk.X * Chunk.Width + coords.X, coords.Y, chunk.Z * Chunk.Depth + coords.Z), - transparentVerticies.Count, out i); - transparentVerticies.AddRange(v); - transparentIndicies.AddRange(i); + TransparentVerticies.Count, out i); + TransparentVerticies.AddRange(v); + TransparentIndicies.AddRange(i); } } var meshes = new Tuple( - new Mesh(Graphics, opaqueVerticies.ToArray(), opaqueIndicies.ToArray(), false), - new Mesh(Graphics, transparentVerticies.ToArray(), transparentIndicies.ToArray(), false)); + new Mesh(Graphics, OpaqueVerticies.ToArray(), OpaqueIndicies.ToArray(), false), + new Mesh(Graphics, TransparentVerticies.ToArray(), TransparentIndicies.ToArray(), false)); meshes.Item1.BoundingBox = boundingBox; meshes.Item2.BoundingBox = boundingBox; return meshes; diff --git a/TrueCraft.Client/Handlers/PacketHandlers.cs b/TrueCraft.Client/Handlers/PacketHandlers.cs index 0916302..6f3be52 100644 --- a/TrueCraft.Client/Handlers/PacketHandlers.cs +++ b/TrueCraft.Client/Handlers/PacketHandlers.cs @@ -4,6 +4,7 @@ using TrueCraft.Core.Networking.Packets; using TrueCraft.Core.Networking; using TrueCraft.Client.Events; using TrueCraft.API; +using System.Diagnostics; namespace TrueCraft.Client.Handlers { @@ -29,7 +30,12 @@ namespace TrueCraft.Client.Handlers public static void HandleHandshake(IPacket _packet, MultiplayerClient client) { var packet = (HandshakeResponsePacket)_packet; - Console.WriteLine("Got handshake with {0}", packet.ConnectionHash); // TODO: Authenticate? + if (packet.ConnectionHash != "-") + { + Console.WriteLine("Online mode is not supported"); + Process.GetCurrentProcess().Kill(); + } + // TODO: Authentication client.QueuePacket(new LoginRequestPacket(PacketReader.Version, "TestUser")); } @@ -44,7 +50,6 @@ namespace TrueCraft.Client.Handlers client._Position = new Vector3(packet.X, packet.Y, packet.Z); client.QueuePacket(packet); client.LoggedIn = true; - Console.WriteLine("Got P+L: {0}", client._Position); // TODO: Pitch and yaw } } diff --git a/TrueCraft.Client/Rendering/Blocks/GrassRenderer.cs b/TrueCraft.Client/Rendering/Blocks/GrassRenderer.cs index 8ebca76..72cfb9d 100644 --- a/TrueCraft.Client/Rendering/Blocks/GrassRenderer.cs +++ b/TrueCraft.Client/Rendering/Blocks/GrassRenderer.cs @@ -16,7 +16,7 @@ namespace TrueCraft.Client.Rendering.Blocks } private static Vector2 TextureMap = new Vector2(0, 0); - private static Vector2 BottomTexture = new Vector2(2, 0); + private static Vector2 EndsTexture = new Vector2(2, 0); private static Vector2 SideTexture = new Vector2(3, 0); private static Vector2[] Texture = { @@ -46,10 +46,10 @@ namespace TrueCraft.Client.Rendering.Blocks TextureMap, TextureMap + Vector2.UnitX, // Negative Y - BottomTexture + Vector2.UnitX + Vector2.UnitY, - BottomTexture + Vector2.UnitY, - BottomTexture, - BottomTexture + Vector2.UnitX, + EndsTexture + Vector2.UnitX + Vector2.UnitY, + EndsTexture + Vector2.UnitY, + EndsTexture, + EndsTexture + Vector2.UnitX, }; public override VertexPositionNormalTexture[] Render(BlockDescriptor descriptor, Vector3 offset, diff --git a/TrueCraft.Client/Rendering/Blocks/LogRenderer.cs b/TrueCraft.Client/Rendering/Blocks/LogRenderer.cs new file mode 100644 index 0000000..e4e863e --- /dev/null +++ b/TrueCraft.Client/Rendering/Blocks/LogRenderer.cs @@ -0,0 +1,60 @@ +using System; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework; +using TrueCraft.Core.Logic.Blocks; +using TrueCraft.API.Logic; + +namespace TrueCraft.Client.Rendering.Blocks +{ + public class LogRenderer : BlockRenderer + { + static LogRenderer() + { + BlockRenderer.RegisterRenderer(WoodBlock.BlockID, new LogRenderer()); + for (int i = 0; i < Texture.Length; i++) + Texture[i] *= new Vector2(16f / 256f); + } + + private static Vector2 EndsTexture = new Vector2(5, 1); + private static Vector2 SideTexture = new Vector2(4, 1); + private static Vector2[] Texture = + { + // Positive Z + SideTexture + Vector2.UnitX + Vector2.UnitY, + SideTexture + Vector2.UnitY, + SideTexture, + SideTexture + Vector2.UnitX, + // Negative Z + SideTexture + Vector2.UnitX + Vector2.UnitY, + SideTexture + Vector2.UnitY, + SideTexture, + SideTexture + Vector2.UnitX, + // Positive X + SideTexture + Vector2.UnitX + Vector2.UnitY, + SideTexture + Vector2.UnitY, + SideTexture, + SideTexture + Vector2.UnitX, + // Negative X + SideTexture + Vector2.UnitX + Vector2.UnitY, + SideTexture + Vector2.UnitY, + SideTexture, + SideTexture + Vector2.UnitX, + // Positive Y + EndsTexture + Vector2.UnitX + Vector2.UnitY, + EndsTexture + Vector2.UnitY, + EndsTexture, + EndsTexture + Vector2.UnitX, + // Negative Y + EndsTexture + Vector2.UnitX + Vector2.UnitY, + EndsTexture + Vector2.UnitY, + EndsTexture, + EndsTexture + Vector2.UnitX, + }; + + public override VertexPositionNormalTexture[] Render(BlockDescriptor descriptor, Vector3 offset, + Tuple textureMap, int indiciesOffset, out int[] indicies) + { + return CreateUniformCube(offset, Texture, indiciesOffset, out indicies); + } + } +} \ No newline at end of file diff --git a/TrueCraft.Client/Rendering/Mesh.cs b/TrueCraft.Client/Rendering/Mesh.cs index 396ea45..4fda103 100644 --- a/TrueCraft.Client/Rendering/Mesh.cs +++ b/TrueCraft.Client/Rendering/Mesh.cs @@ -15,7 +15,7 @@ namespace TrueCraft.Client.Rendering public Mesh(GraphicsDevice device, VertexPositionNormalTexture[] verticies, int[] indicies, bool calculateBounds = true) { - Empty = verticies.Length == 0 || indicies.Length == 0; + Empty = verticies.Length == 0 && indicies.Length == 0; if (!Empty) { Verticies = new VertexBuffer(device, VertexPositionNormalTexture.VertexDeclaration, diff --git a/TrueCraft.Client/TrueCraft.Client.csproj b/TrueCraft.Client/TrueCraft.Client.csproj index 9fe1e3f..45c00f1 100644 --- a/TrueCraft.Client/TrueCraft.Client.csproj +++ b/TrueCraft.Client/TrueCraft.Client.csproj @@ -87,6 +87,7 @@ + diff --git a/TrueCraft.Client/TrueCraftGame.cs b/TrueCraft.Client/TrueCraftGame.cs index 8ddbf04..527065a 100644 --- a/TrueCraft.Client/TrueCraftGame.cs +++ b/TrueCraft.Client/TrueCraftGame.cs @@ -12,6 +12,7 @@ using System.Linq; using System.ComponentModel; using TrueCraft.Core.Networking.Packets; using TrueCraft.API.World; +using System.Collections.Concurrent; namespace TrueCraft.Client { @@ -26,8 +27,10 @@ namespace TrueCraft.Client private ChunkConverter ChunkConverter { get; set; } private DateTime NextPhysicsUpdate { get; set; } private List ChunkMeshes { get; set; } + private ConcurrentBag PendingMainThreadActions { get; set; } + private ConcurrentBag IncomingChunks { get; set; } + private ConcurrentBag IncomingTransparentChunks { get; set; } private List TransparentChunkMeshes { get; set; } - private readonly object ChunkMeshesLock = new object(); private Matrix Camera; private Matrix Perspective; private BoundingFrustum CameraView; @@ -49,6 +52,9 @@ namespace TrueCraft.Client NextPhysicsUpdate = DateTime.MinValue; ChunkMeshes = new List(); TransparentChunkMeshes = new List(); + IncomingChunks = new ConcurrentBag(); + IncomingTransparentChunks = new ConcurrentBag(); + PendingMainThreadActions = new ConcurrentBag(); MouseCaptured = true; } @@ -61,11 +67,8 @@ namespace TrueCraft.Client Client.ChunkLoaded += (sender, e) => ChunkConverter.QueueChunk(e.Chunk); ChunkConverter.Start((opaque, transparent) => { - lock (ChunkMeshesLock) - { - ChunkMeshes.Add(opaque); - TransparentChunkMeshes.Add(transparent); - } + IncomingChunks.Add(opaque); + IncomingTransparentChunks.Add(transparent); }); Client.PropertyChanged += HandleClientPropertyChanged; Client.Connect(EndPoint); @@ -84,8 +87,7 @@ namespace TrueCraft.Client UpdateMatricies(); var sorter = new ChunkConverter.ChunkSorter(new Coordinates3D( (int)Client.Position.X, 0, (int)Client.Position.Z)); - lock (ChunkMeshesLock) - TransparentChunkMeshes.Sort(sorter); + PendingMainThreadActions.Add(() => TransparentChunkMeshes.Sort(sorter)); break; } } @@ -128,9 +130,12 @@ namespace TrueCraft.Client { if (state.IsKeyDown(Keys.Escape)) Exit(); + // TODO: Rebindable keys // TODO: Horizontal terrain collisions + Microsoft.Xna.Framework.Vector3 delta = Microsoft.Xna.Framework.Vector3.Zero; + if (state.IsKeyDown(Keys.Left) || state.IsKeyDown(Keys.A)) delta += Microsoft.Xna.Framework.Vector3.Left; if (state.IsKeyDown(Keys.Right) || state.IsKeyDown(Keys.D)) @@ -140,14 +145,16 @@ namespace TrueCraft.Client if (state.IsKeyDown(Keys.Down) || state.IsKeyDown(Keys.S)) delta += Microsoft.Xna.Framework.Vector3.Backward; + if (delta != Microsoft.Xna.Framework.Vector3.Zero) + { + var lookAt = Microsoft.Xna.Framework.Vector3.Transform( + delta, Matrix.CreateRotationY(MathHelper.ToRadians(Client.Yaw))); + + Client.Position += new TrueCraft.API.Vector3(lookAt.X, lookAt.Y, lookAt.Z) * (gameTime.ElapsedGameTime.TotalSeconds * 4.3717); + } + if (state.IsKeyUp(Keys.Tab) && oldState.IsKeyDown(Keys.Tab)) MouseCaptured = !MouseCaptured; - - var lookAt = Microsoft.Xna.Framework.Vector3.Transform( - delta, Matrix.CreateRotationY(MathHelper.ToRadians(Client.Yaw))); - - Client.Position += new TrueCraft.API.Vector3(lookAt.X, lookAt.Y, lookAt.Z) * (gameTime.ElapsedGameTime.TotalSeconds * 4.3717); - if (MouseCaptured) { var centerX = GraphicsDevice.Viewport.Width / 2; @@ -172,25 +179,31 @@ namespace TrueCraft.Client { i.Update(gameTime); } - if (NextPhysicsUpdate < DateTime.Now) + + Mesh mesh; + if (IncomingChunks.TryTake(out mesh)) + ChunkMeshes.Add(mesh); + if (IncomingTransparentChunks.TryTake(out mesh)) + TransparentChunkMeshes.Add(mesh); + Action action; + if (PendingMainThreadActions.TryTake(out action)) + action(); + + if (NextPhysicsUpdate < DateTime.Now && Client.LoggedIn) { IChunk chunk; var adjusted = Client.World.World.FindBlockPosition(new Coordinates3D((int)Client.Position.X, 0, (int)Client.Position.Z), out chunk); - if (Client.LoggedIn && chunk != null) + if (chunk != null) { if (chunk.GetHeight((byte)adjusted.X, (byte)adjusted.Z) != 0) Client.Physics.Update(); } - if (Client.LoggedIn) - { - // NOTE: This is to make the vanilla server send us chunk packets - // We should eventually make some means of detecing that we're on a vanilla server to enable this - // It's a waste of bandwidth to do it on a TrueCraft server - Console.WriteLine("Sending position packet"); - Client.QueuePacket(new PlayerGroundedPacket { OnGround = true }); - Client.QueuePacket(new PlayerPositionAndLookPacket(Client.Position.X, Client.Position.Y, - Client.Position.Y + MultiplayerClient.Height, Client.Position.Z, Client.Yaw, Client.Pitch, false)); - } + // NOTE: This is to make the vanilla server send us chunk packets + // We should eventually make some means of detecing that we're on a vanilla server to enable this + // It's a waste of bandwidth to do it on a TrueCraft server + Client.QueuePacket(new PlayerGroundedPacket { OnGround = true }); + Client.QueuePacket(new PlayerPositionAndLookPacket(Client.Position.X, Client.Position.Y, + Client.Position.Y + MultiplayerClient.Height, Client.Position.Z, Client.Yaw, Client.Pitch, false)); NextPhysicsUpdate = DateTime.Now.AddMilliseconds(1000 / 20); } var state = Keyboard.GetState(); @@ -217,6 +230,10 @@ namespace TrueCraft.Client Perspective = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(70f), GraphicsDevice.Viewport.AspectRatio, 0.01f, 1000f); CameraView = new BoundingFrustum(Camera * Perspective); + + OpaqueEffect.View = TransparentEffect.View = Camera; + OpaqueEffect.Projection = TransparentEffect.Projection = Perspective; + OpaqueEffect.World = TransparentEffect.World = Matrix.Identity; } protected override void Draw(GameTime gameTime) @@ -226,45 +243,40 @@ namespace TrueCraft.Client GraphicsDevice.SamplerStates[1] = SamplerState.PointClamp; GraphicsDevice.BlendState = BlendState.AlphaBlend; - OpaqueEffect.View = TransparentEffect.View = Camera; - OpaqueEffect.Projection = TransparentEffect.Projection = Perspective; - OpaqueEffect.World = TransparentEffect.World = Matrix.Identity; int verticies = 0, chunks = 0; - lock (ChunkMeshesLock) + GraphicsDevice.DepthStencilState = DepthStencilState.Default; + for (int i = 0; i < ChunkMeshes.Count; i++) { - GraphicsDevice.DepthStencilState = DepthStencilState.Default; - foreach (var chunk in ChunkMeshes) + if (CameraView.Intersects(ChunkMeshes[i].BoundingBox)) { - if (CameraView.Intersects(chunk.BoundingBox)) - { - verticies += chunk.Verticies.VertexCount; - chunks++; - chunk.Draw(OpaqueEffect); - } + verticies += ChunkMeshes[i].Verticies.VertexCount; + chunks++; + ChunkMeshes[i].Draw(OpaqueEffect); } - GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; - foreach (var chunk in TransparentChunkMeshes) + } + GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; + for (int i = 0; i < TransparentChunkMeshes.Count; i++) + { + if (CameraView.Intersects(TransparentChunkMeshes[i].BoundingBox)) { - if (CameraView.Intersects(chunk.BoundingBox)) - { - if (chunk.Verticies != null) - verticies += chunk.Verticies.VertexCount; - chunk.Draw(TransparentEffect); - } + if (TransparentChunkMeshes[i].Verticies != null) + verticies += TransparentChunkMeshes[i].Verticies.VertexCount; + TransparentChunkMeshes[i].Draw(TransparentEffect); } } GraphicsDevice.DepthStencilState = DepthStencilState.Default; SpriteBatch.Begin(); - foreach (var i in Interfaces) + for (int i = 0; i < Interfaces.Count; i++) { - i.DrawSprites(gameTime, SpriteBatch); + Interfaces[i].DrawSprites(gameTime, SpriteBatch); } int fps = (int)(1 / gameTime.ElapsedGameTime.TotalSeconds); DejaVu.DrawText(SpriteBatch, 0, GraphicsDevice.Viewport.Height - 30, string.Format("{0} FPS, {1} verticies, {2} chunks", fps + 1, verticies, chunks)); SpriteBatch.End(); + base.Draw(gameTime); } }