Add chunk mesh generation and basic rendering
Woot!
This commit is contained in:
parent
0149af3ba2
commit
9385568ab7
README.md
TrueCraft.Client.Linux
@ -88,6 +88,12 @@ smaller changes like lighting improvements and bow usage mechanics.
|
||||
Finally, if we've got a nice mature project and a good community going, modding
|
||||
support would be great.
|
||||
|
||||
## Textures
|
||||
|
||||
TrueCraft currently distributes
|
||||
[ProgrammerArt](https://github.com/deathcap/ProgrammerArt) as the default
|
||||
texture pack.
|
||||
|
||||
## Blah blah blah
|
||||
|
||||
TrueCraft is not associated with Mojang or Minecraft in any sort of official
|
||||
|
@ -2,6 +2,15 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using System.Linq;
|
||||
using TrueCraft.Client.Linux.Rendering;
|
||||
using TrueCraft.Core.World;
|
||||
using TrueCraft.API;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Vector2 = Microsoft.Xna.Framework.Vector2;
|
||||
using Vector3 = Microsoft.Xna.Framework.Vector3;
|
||||
using TrueCraft.API.Logic;
|
||||
|
||||
namespace TrueCraft.Client.Linux
|
||||
{
|
||||
@ -11,13 +20,20 @@ namespace TrueCraft.Client.Linux
|
||||
/// </summary>
|
||||
public class ChunkConverter
|
||||
{
|
||||
public delegate void ChunkConsumer(Mesh mesh);
|
||||
|
||||
private ConcurrentQueue<ReadOnlyChunk> ChunkQueue { get; set; }
|
||||
private Thread ChunkWorker { get; set; }
|
||||
private GraphicsDevice Graphics { get; set; }
|
||||
private IBlockRepository BlockRepository { get; set; }
|
||||
private ChunkConsumer Consumer { get; set; }
|
||||
|
||||
public ChunkConverter()
|
||||
public ChunkConverter(GraphicsDevice graphics, IBlockRepository blockRepository)
|
||||
{
|
||||
ChunkQueue = new ConcurrentQueue<ReadOnlyChunk>();
|
||||
ChunkWorker = new Thread(new ThreadStart(DoChunks));
|
||||
BlockRepository = blockRepository;
|
||||
Graphics = graphics;
|
||||
}
|
||||
|
||||
public void QueueChunk(ReadOnlyChunk chunk)
|
||||
@ -25,8 +41,9 @@ namespace TrueCraft.Client.Linux
|
||||
ChunkQueue.Enqueue(chunk);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
public void Start(ChunkConsumer consumer)
|
||||
{
|
||||
Consumer = consumer;
|
||||
ChunkWorker.Start();
|
||||
}
|
||||
|
||||
@ -43,13 +60,75 @@ namespace TrueCraft.Client.Linux
|
||||
ReadOnlyChunk chunk;
|
||||
if (ChunkQueue.Any())
|
||||
{
|
||||
while (!ChunkQueue.TryDequeue(out chunk)) { }
|
||||
// TODO: Create verticies from chunk
|
||||
Console.WriteLine("Chunk worker received chunk at {0}, {1}", chunk.X, chunk.Z);
|
||||
while (!ChunkQueue.TryDequeue(out chunk))
|
||||
{
|
||||
}
|
||||
var mesh = ProcessChunk(chunk);
|
||||
Consumer(mesh);
|
||||
}
|
||||
if (idle)
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
private Mesh ProcessChunk(ReadOnlyChunk chunk)
|
||||
{
|
||||
var verticies = new List<VertexPositionNormalTexture>();
|
||||
var indicies = new List<int>();
|
||||
for (byte x = 0; x < Chunk.Width; x++)
|
||||
{
|
||||
for (byte z = 0; z < Chunk.Depth; z++)
|
||||
{
|
||||
//var height = chunk.Chunk.GetHeight(x, z);
|
||||
for (byte y = 0; y < Chunk.Height; y++)
|
||||
{
|
||||
var coords = new Coordinates3D(x, y, z);
|
||||
var id = chunk.GetBlockId(coords);
|
||||
var provider = BlockRepository.GetBlockProvider(id);
|
||||
var textureMap = provider.GetTextureMap(chunk.GetMetadata(coords));
|
||||
if (textureMap == null)
|
||||
{
|
||||
// TODO: handle this better
|
||||
textureMap = new Tuple<int, int>(0, 0);
|
||||
}
|
||||
if (id != 0)
|
||||
{
|
||||
int[] i;
|
||||
var v = CreateUniformCube(new Vector3(chunk.X * Chunk.Width + x, y, chunk.Z * Chunk.Depth + z),
|
||||
textureMap, indicies.Count, out i);
|
||||
verticies.AddRange(v);
|
||||
indicies.AddRange(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Console.WriteLine("Created mesh for {0}, {0}", chunk.X, chunk.Z);
|
||||
return new Mesh(Graphics, verticies.ToArray(), indicies.ToArray());
|
||||
}
|
||||
|
||||
private VertexPositionNormalTexture[] CreateUniformCube(Vector3 offset, Tuple<int, int> textureMap, int indiciesOffset, out int[] indicies)
|
||||
{
|
||||
var texCoords = new Vector2(textureMap.Item1, textureMap.Item2);
|
||||
var texture = new[]
|
||||
{
|
||||
texCoords + Vector2.UnitX + Vector2.UnitY,
|
||||
texCoords + Vector2.UnitY,
|
||||
texCoords,
|
||||
texCoords + Vector2.UnitX
|
||||
};
|
||||
for (int i = 0; i < texture.Length; i++)
|
||||
texture[i] *= new Vector2(16f / 256f);
|
||||
indicies = new int[6 * 6];
|
||||
var verticies = new VertexPositionNormalTexture[4 * 6];
|
||||
int[] _indicies;
|
||||
for (int _side = 0; _side < 6; _side++)
|
||||
{
|
||||
var side = (Mesh.CubeFace)_side;
|
||||
var quad = Mesh.CreateQuad(side, offset, texture, indiciesOffset, out _indicies);
|
||||
Array.Copy(quad, 0, verticies, _side * 4, 4);
|
||||
Array.Copy(_indicies, 0, indicies, _side * 6, 6);
|
||||
}
|
||||
return verticies;
|
||||
}
|
||||
}
|
||||
}
|
BIN
TrueCraft.Client.Linux/Content/items.png
Normal file
BIN
TrueCraft.Client.Linux/Content/items.png
Normal file
Binary file not shown.
After (image error) Size: 48 KiB |
BIN
TrueCraft.Client.Linux/Content/pack.png
Normal file
BIN
TrueCraft.Client.Linux/Content/pack.png
Normal file
Binary file not shown.
After (image error) Size: 3.1 KiB |
BIN
TrueCraft.Client.Linux/Content/terrain.png
Normal file
BIN
TrueCraft.Client.Linux/Content/terrain.png
Normal file
Binary file not shown.
After (image error) Size: 42 KiB |
@ -160,6 +160,9 @@ namespace TrueCraft.Client.Linux
|
||||
Position = newPosition;
|
||||
}
|
||||
|
||||
public float Yaw { get; set; }
|
||||
public float Pitch { get; set; }
|
||||
|
||||
internal Vector3 _Position;
|
||||
public Vector3 Position
|
||||
{
|
||||
@ -170,7 +173,7 @@ namespace TrueCraft.Client.Linux
|
||||
set
|
||||
{
|
||||
if (_Position != value)
|
||||
QueuePacket(new PlayerPositionAndLookPacket(value.X, value.Y, value.Y + Height, value.Z, 0, 0, false));
|
||||
QueuePacket(new PlayerPositionAndLookPacket(value.X, value.Y, value.Y + Height, value.Z, Yaw, Pitch, false));
|
||||
_Position = value;
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ namespace TrueCraft.Client.Linux
|
||||
UnloadChunks = true;
|
||||
}
|
||||
|
||||
public short GetBlockID(Coordinates3D coordinates)
|
||||
public byte GetBlockID(Coordinates3D coordinates)
|
||||
{
|
||||
return World.GetBlockID(coordinates);
|
||||
}
|
||||
@ -89,7 +89,7 @@ namespace TrueCraft.Client.Linux
|
||||
Chunk = chunk;
|
||||
}
|
||||
|
||||
public short GetBlockId(Coordinates3D coordinates)
|
||||
public byte GetBlockId(Coordinates3D coordinates)
|
||||
{
|
||||
return Chunk.GetBlockID(coordinates);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace TrueCraft.Client.Linux.Rendering
|
||||
{
|
||||
@ -11,9 +12,9 @@ namespace TrueCraft.Client.Linux.Rendering
|
||||
public Mesh(GraphicsDevice device, VertexPositionNormalTexture[] verticies, int[] indicies)
|
||||
{
|
||||
Verticies = new VertexBuffer(device, VertexPositionNormalTexture.VertexDeclaration,
|
||||
verticies.Length, BufferUsage.None);
|
||||
verticies.Length, BufferUsage.WriteOnly);
|
||||
Verticies.SetData(verticies);
|
||||
Indicies = new IndexBuffer(device, IndexElementSize.ThirtyTwoBits, indicies.Length, BufferUsage.None);
|
||||
Indicies = new IndexBuffer(device, typeof(int), indicies.Length, BufferUsage.WriteOnly);
|
||||
Indicies.SetData(indicies);
|
||||
}
|
||||
|
||||
@ -36,5 +37,96 @@ namespace TrueCraft.Client.Linux.Rendering
|
||||
0, 0, Indicies.IndexCount, 0, Indicies.IndexCount / 3);
|
||||
}
|
||||
}
|
||||
|
||||
public static VertexPositionNormalTexture[] CreateQuad(CubeFace face, Vector3 offset, Vector2[] texture, int indiciesOffset, out int[] indicies)
|
||||
{
|
||||
indicies = new[] { 1, 0, 3, 1, 3, 2 };
|
||||
for (int i = 0; i < indicies.Length; i++)
|
||||
indicies[i] += ((int)face * 6) + indiciesOffset;
|
||||
var quad = new VertexPositionNormalTexture[4];
|
||||
var unit = CubeMesh[(int)face];
|
||||
var normal = CubeNormals[(int)face];
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
quad[i] = new VertexPositionNormalTexture(offset + unit[i], normal, texture[i]);
|
||||
}
|
||||
return quad;
|
||||
}
|
||||
|
||||
public enum CubeFace
|
||||
{
|
||||
PositiveZ = 0,
|
||||
NegativeZ,
|
||||
PositiveX,
|
||||
NegativeX,
|
||||
PositiveY,
|
||||
NegativeY
|
||||
}
|
||||
|
||||
private static Vector3[][] CubeMesh;
|
||||
|
||||
private static readonly Vector3[] CubeNormals =
|
||||
{
|
||||
new Vector3(0, 0, 1),
|
||||
new Vector3(0, 0, -1),
|
||||
new Vector3(1, 0, 0),
|
||||
new Vector3(-1, 0, 0),
|
||||
new Vector3(0, 1, 0),
|
||||
new Vector3(0, -1, 0)
|
||||
};
|
||||
|
||||
static Mesh()
|
||||
{
|
||||
CubeMesh = new Vector3[6][];
|
||||
|
||||
CubeMesh[0] = new[] // Positive Z face
|
||||
{
|
||||
new Vector3(0.5f, 0.5f, 0.5f),
|
||||
new Vector3(-0.5f, 0.5f, 0.5f),
|
||||
new Vector3(-0.5f, -0.5f, 0.5f),
|
||||
new Vector3(0.5f, -0.5f, 0.5f)
|
||||
};
|
||||
|
||||
CubeMesh[1] = new[] // Negative Z face
|
||||
{
|
||||
new Vector3(-0.5f, 0.5f, -0.5f),
|
||||
new Vector3(0.5f, 0.5f, -0.5f),
|
||||
new Vector3(0.5f, -0.5f, -0.5f),
|
||||
new Vector3(-0.5f, -0.5f, -0.5f)
|
||||
};
|
||||
|
||||
CubeMesh[2] = new[] // Positive X face
|
||||
{
|
||||
new Vector3(0.5f, 0.5f, -0.5f),
|
||||
new Vector3(0.5f, 0.5f, 0.5f),
|
||||
new Vector3(0.5f, -0.5f, 0.5f),
|
||||
new Vector3(0.5f, -0.5f, -0.5f)
|
||||
};
|
||||
|
||||
CubeMesh[3] = new[] // Negative X face
|
||||
{
|
||||
new Vector3(-0.5f, 0.5f, 0.5f),
|
||||
new Vector3(-0.5f, 0.5f, -0.5f),
|
||||
new Vector3(-0.5f, -0.5f, -0.5f),
|
||||
new Vector3(-0.5f, -0.5f, 0.5f)
|
||||
};
|
||||
|
||||
CubeMesh[4] = new[] // Positive Y face
|
||||
{
|
||||
new Vector3(-0.5f, 0.5f, -0.5f),
|
||||
new Vector3(-0.5f, 0.5f, 0.5f),
|
||||
new Vector3(0.5f, 0.5f, 0.5f),
|
||||
new Vector3(0.5f, 0.5f, -0.5f)
|
||||
};
|
||||
|
||||
CubeMesh[5] = new[] // Negative Y face
|
||||
{
|
||||
new Vector3(0.5f, -0.5f, -0.5f),
|
||||
new Vector3(0.5f, -0.5f, 0.5f),
|
||||
new Vector3(-0.5f, -0.5f, 0.5f),
|
||||
new Vector3(-0.5f, -0.5f, -0.5f)
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -118,5 +118,11 @@
|
||||
<None Include="Content\dejavu.fnt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Content\items.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Content\terrain.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -7,6 +7,7 @@ using TrueCraft.Client.Linux.Interface;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using TrueCraft.API;
|
||||
using TrueCraft.Client.Linux.Rendering;
|
||||
|
||||
namespace TrueCraft.Client.Linux
|
||||
{
|
||||
@ -20,6 +21,10 @@ namespace TrueCraft.Client.Linux
|
||||
private IPEndPoint EndPoint { get; set; }
|
||||
private ChunkConverter ChunkConverter { get; set; }
|
||||
private DateTime NextPhysicsUpdate { get; set; }
|
||||
private List<Mesh> ChunkMeshes { get; set; }
|
||||
private object ChunkMeshesLock = new object();
|
||||
|
||||
private BasicEffect effect;
|
||||
|
||||
public TrueCraftGame(MultiplayerClient client, IPEndPoint endPoint)
|
||||
{
|
||||
@ -31,9 +36,8 @@ namespace TrueCraft.Client.Linux
|
||||
Graphics.PreferredBackBufferHeight = 720;
|
||||
Client = client;
|
||||
EndPoint = endPoint;
|
||||
ChunkConverter = new ChunkConverter();
|
||||
Client.ChunkLoaded += (sender, e) => ChunkConverter.QueueChunk(e.Chunk);
|
||||
NextPhysicsUpdate = DateTime.MinValue;
|
||||
ChunkMeshes = new List<Mesh>();
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
@ -41,8 +45,17 @@ namespace TrueCraft.Client.Linux
|
||||
Interfaces = new List<IGameInterface>();
|
||||
SpriteBatch = new SpriteBatch(GraphicsDevice);
|
||||
base.Initialize(); // (calls LoadContent)
|
||||
ChunkConverter.Start();
|
||||
ChunkConverter = new ChunkConverter(Graphics.GraphicsDevice, Client.World.World.BlockRepository);
|
||||
Client.ChunkLoaded += (sender, e) => ChunkConverter.QueueChunk(e.Chunk);
|
||||
ChunkConverter.Start(mesh =>
|
||||
{
|
||||
lock (ChunkMeshesLock)
|
||||
ChunkMeshes.Add(mesh);
|
||||
});
|
||||
Client.Connect(EndPoint);
|
||||
var centerX = GraphicsDevice.Viewport.Width / 2;
|
||||
var centerY = GraphicsDevice.Viewport.Height / 2;
|
||||
Mouse.SetPosition(centerX, centerY);
|
||||
}
|
||||
|
||||
protected override void LoadContent()
|
||||
@ -53,6 +66,9 @@ namespace TrueCraft.Client.Linux
|
||||
var fontTexture = Content.Load<Texture2D>("dejavu_0.png");
|
||||
DejaVu = new FontRenderer(fontFile, fontTexture);
|
||||
Interfaces.Add(new ChatInterface(Client, DejaVu));
|
||||
effect = new BasicEffect(GraphicsDevice);
|
||||
effect.TextureEnabled = true;
|
||||
effect.Texture = Texture2D.FromStream(GraphicsDevice, File.OpenRead("Content/terrain.png"));
|
||||
base.LoadContent();
|
||||
}
|
||||
|
||||
@ -66,7 +82,6 @@ namespace TrueCraft.Client.Linux
|
||||
{
|
||||
if (state.IsKeyDown(Keys.Escape))
|
||||
Exit();
|
||||
// TODO: Handle rotation
|
||||
// TODO: Rebindable keys
|
||||
// TODO: Horizontal terrain collisions
|
||||
TrueCraft.API.Vector3 delta = TrueCraft.API.Vector3.Zero;
|
||||
@ -79,6 +94,16 @@ namespace TrueCraft.Client.Linux
|
||||
if (state.IsKeyDown(Keys.Down) || state.IsKeyDown(Keys.S))
|
||||
delta = TrueCraft.API.Vector3.Backwards;
|
||||
Client.Position += delta * (gameTime.ElapsedGameTime.TotalSeconds * 4.3717); // Note: 4.3717 is the speed of a Minecraft player in m/s
|
||||
|
||||
var centerX = GraphicsDevice.Viewport.Width / 2;
|
||||
var centerY = GraphicsDevice.Viewport.Height / 2;
|
||||
var mouse = Mouse.GetState();
|
||||
var look = new Vector2(centerX - mouse.Position.X, centerY - mouse.Position.Y) * 0.2f; // TODO: fewer magic numbers
|
||||
Mouse.SetPosition(centerX, centerY);
|
||||
Client.Yaw += look.X;
|
||||
Client.Pitch += look.Y;
|
||||
Client.Yaw %= 360;
|
||||
Client.Pitch %= 360;
|
||||
}
|
||||
|
||||
protected override void Update(GameTime gameTime)
|
||||
@ -99,12 +124,41 @@ namespace TrueCraft.Client.Linux
|
||||
|
||||
protected override void Draw(GameTime gameTime)
|
||||
{
|
||||
// TODO: Move camera logic elsewhere
|
||||
var player = new Microsoft.Xna.Framework.Vector3(
|
||||
(float)Client.Position.X,
|
||||
(float)Client.Position.Y,
|
||||
(float)Client.Position.Z);
|
||||
|
||||
var lookAt = Microsoft.Xna.Framework.Vector3.Transform(
|
||||
new Microsoft.Xna.Framework.Vector3(0, 0, -1),
|
||||
Matrix.CreateRotationX(MathHelper.ToRadians(Client.Pitch)) * Matrix.CreateRotationY(MathHelper.ToRadians(Client.Yaw)));
|
||||
|
||||
var cameraMatrix = Matrix.CreateLookAt(
|
||||
player, player + lookAt,
|
||||
Microsoft.Xna.Framework.Vector3.Up);
|
||||
|
||||
var projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(60f), GraphicsDevice.Viewport.AspectRatio, 0.3f, 10000f);
|
||||
|
||||
Graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
|
||||
Graphics.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
|
||||
|
||||
effect.View = cameraMatrix;
|
||||
effect.Projection = projectionMatrix;
|
||||
effect.World = Matrix.CreateTranslation(Microsoft.Xna.Framework.Vector3.Zero);
|
||||
lock (ChunkMeshesLock)
|
||||
{
|
||||
foreach (var chunk in ChunkMeshes)
|
||||
chunk.Draw(effect);
|
||||
}
|
||||
|
||||
SpriteBatch.Begin();
|
||||
foreach (var i in Interfaces)
|
||||
{
|
||||
i.DrawSprites(gameTime, SpriteBatch);
|
||||
}
|
||||
DejaVu.DrawText(SpriteBatch, 0, 500, string.Format("X: {0}, Y: {1}, Z: {2}", Client.Position.X, Client.Position.Y, Client.Position.Z));
|
||||
DejaVu.DrawText(SpriteBatch, 0, 530, string.Format("Yaw: {0}, Pitch: {1}", Client.Yaw, Client.Pitch));
|
||||
SpriteBatch.End();
|
||||
base.Draw(gameTime);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user