using System; 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 { /// /// A daemon of sorts that creates meshes from chunks. /// Passing meshes back is NOT thread-safe. /// public class ChunkConverter { public class ChunkSorter : Comparer { public Coordinates3D Camera { get; set; } public ChunkSorter(Coordinates3D camera) { Camera = camera; } public override int Compare(Mesh _x, Mesh _y) { var x = (ReadOnlyChunk)_x.Data; var y = (ReadOnlyChunk)_y.Data; return (int)(new Coordinates3D(y.X * Chunk.Width, 0, y.Z * Chunk.Depth).DistanceTo(Camera) - new Coordinates3D(x.X * Chunk.Width, 0, x.Z * Chunk.Depth).DistanceTo(Camera)); } } public delegate void ChunkConsumer(Mesh opaqueMesh, Mesh transparentMesh); private ConcurrentQueue 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(GraphicsDevice graphics, IBlockRepository blockRepository) { ChunkQueue = new ConcurrentQueue(); ChunkWorker = new Thread(new ThreadStart(DoChunks)); ChunkWorker.IsBackground = true; ChunkWorker.Priority = ThreadPriority.Lowest; BlockRepository = blockRepository; Graphics = graphics; } public void QueueChunk(ReadOnlyChunk chunk) { ChunkQueue.Enqueue(chunk); } public void Start(ChunkConsumer consumer) { Consumer = consumer; ChunkWorker.Start(); } public void Stop() { ChunkWorker.Abort(); } private void DoChunks() { while (true) { ReadOnlyChunk chunk; if (ChunkQueue.Any()) { while (!ChunkQueue.TryDequeue(out chunk)) { } var mesh = ProcessChunk(chunk); mesh.Item1.Data = chunk; mesh.Item2.Data = chunk; Consumer(mesh.Item1, mesh.Item2); } Thread.Yield(); } } private static readonly Coordinates3D[] AdjacentCoordinates = { Coordinates3D.Up, Coordinates3D.Down, Coordinates3D.North, Coordinates3D.South, Coordinates3D.East, Coordinates3D.West }; 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(); var boundingBox = new Microsoft.Xna.Framework.BoundingBox( new Vector3(chunk.X * Chunk.Width, 0, chunk.Z * Chunk.Depth), new Vector3(chunk.X * Chunk.Width + Chunk.Width, Chunk.Height, chunk.Z * Chunk.Depth + Chunk.Depth)); for (byte x = 0; x < Chunk.Width; x++) { for (byte z = 0; z < Chunk.Depth; 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); if (id != 0) drawableCoordinates.Add(coords); if (!provider.Opaque) { // Add adjacent blocks foreach (var a in AdjacentCoordinates) { var next = coords + a; if (next.X < 0 || next.X >= Chunk.Width || next.Y < 0 || next.Y >= Chunk.Height || next.Z < 0 || next.Z >= Chunk.Depth) { continue; } if (chunk.GetBlockId(next) != 0) drawableCoordinates.Add(next); } } } } } foreach (var coords in drawableCoordinates) { var descriptor = new BlockDescriptor { ID = chunk.GetBlockId(coords), Metadata = chunk.GetMetadata(coords), BlockLight = chunk.GetBlockLight(coords), SkyLight = chunk.GetSkyLight(coords), Coordinates = coords }; var provider = BlockRepository.GetBlockProvider(descriptor.ID); if (provider.RenderOpaque) { 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); } 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); } } var meshes = new Tuple( 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; } } }