2015-05-13 14:20:35 -06:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Linq;
|
2015-05-13 23:09:49 -06:00
|
|
|
|
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;
|
2015-05-13 14:20:35 -06:00
|
|
|
|
|
|
|
|
|
namespace TrueCraft.Client.Linux
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// A daemon of sorts that creates meshes from chunks.
|
|
|
|
|
/// Passing meshes back is NOT thread-safe.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class ChunkConverter
|
|
|
|
|
{
|
2015-05-14 18:47:16 -06:00
|
|
|
|
public class ChunkSorter : Comparer<Mesh>
|
|
|
|
|
{
|
|
|
|
|
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);
|
2015-05-13 23:09:49 -06:00
|
|
|
|
|
2015-05-13 14:20:35 -06:00
|
|
|
|
private ConcurrentQueue<ReadOnlyChunk> ChunkQueue { get; set; }
|
|
|
|
|
private Thread ChunkWorker { get; set; }
|
2015-05-13 23:09:49 -06:00
|
|
|
|
private GraphicsDevice Graphics { get; set; }
|
|
|
|
|
private IBlockRepository BlockRepository { get; set; }
|
|
|
|
|
private ChunkConsumer Consumer { get; set; }
|
2015-05-13 14:20:35 -06:00
|
|
|
|
|
2015-05-13 23:09:49 -06:00
|
|
|
|
public ChunkConverter(GraphicsDevice graphics, IBlockRepository blockRepository)
|
2015-05-13 14:20:35 -06:00
|
|
|
|
{
|
|
|
|
|
ChunkQueue = new ConcurrentQueue<ReadOnlyChunk>();
|
|
|
|
|
ChunkWorker = new Thread(new ThreadStart(DoChunks));
|
2015-05-13 23:45:13 -06:00
|
|
|
|
ChunkWorker.IsBackground = true;
|
|
|
|
|
ChunkWorker.Priority = ThreadPriority.Lowest;
|
2015-05-13 23:09:49 -06:00
|
|
|
|
BlockRepository = blockRepository;
|
|
|
|
|
Graphics = graphics;
|
2015-05-13 14:20:35 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void QueueChunk(ReadOnlyChunk chunk)
|
|
|
|
|
{
|
|
|
|
|
ChunkQueue.Enqueue(chunk);
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-13 23:09:49 -06:00
|
|
|
|
public void Start(ChunkConsumer consumer)
|
2015-05-13 14:20:35 -06:00
|
|
|
|
{
|
2015-05-13 23:09:49 -06:00
|
|
|
|
Consumer = consumer;
|
2015-05-13 14:20:35 -06:00
|
|
|
|
ChunkWorker.Start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Stop()
|
|
|
|
|
{
|
|
|
|
|
ChunkWorker.Abort();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DoChunks()
|
|
|
|
|
{
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
ReadOnlyChunk chunk;
|
|
|
|
|
if (ChunkQueue.Any())
|
|
|
|
|
{
|
2015-05-14 18:47:16 -06:00
|
|
|
|
while (!ChunkQueue.TryDequeue(out chunk)) { }
|
2015-05-13 23:09:49 -06:00
|
|
|
|
var mesh = ProcessChunk(chunk);
|
2015-05-14 18:47:16 -06:00
|
|
|
|
mesh.Item1.Data = chunk;
|
|
|
|
|
mesh.Item2.Data = chunk;
|
|
|
|
|
Consumer(mesh.Item1, mesh.Item2);
|
2015-05-13 14:20:35 -06:00
|
|
|
|
}
|
2015-05-14 21:12:11 -06:00
|
|
|
|
Thread.Yield();
|
2015-05-13 14:20:35 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-05-13 23:09:49 -06:00
|
|
|
|
|
2015-05-15 15:25:09 -06:00
|
|
|
|
private static readonly Coordinates3D[] AdjacentCoordinates =
|
|
|
|
|
{
|
|
|
|
|
Coordinates3D.Up,
|
|
|
|
|
Coordinates3D.Down,
|
|
|
|
|
Coordinates3D.North,
|
|
|
|
|
Coordinates3D.South,
|
|
|
|
|
Coordinates3D.East,
|
|
|
|
|
Coordinates3D.West
|
|
|
|
|
};
|
|
|
|
|
|
2015-05-14 18:47:16 -06:00
|
|
|
|
private Tuple<Mesh, Mesh> ProcessChunk(ReadOnlyChunk chunk)
|
2015-05-13 23:09:49 -06:00
|
|
|
|
{
|
2015-05-14 18:47:16 -06:00
|
|
|
|
var opaqueVerticies = new List<VertexPositionNormalTexture>();
|
|
|
|
|
var opaqueIndicies = new List<int>();
|
|
|
|
|
|
|
|
|
|
var transparentVerticies = new List<VertexPositionNormalTexture>();
|
|
|
|
|
var transparentIndicies = new List<int>();
|
|
|
|
|
|
2015-05-15 15:25:09 -06:00
|
|
|
|
var drawableCoordinates = new HashSet<Coordinates3D>();
|
|
|
|
|
|
2015-05-15 15:48:20 -06:00
|
|
|
|
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));
|
|
|
|
|
|
2015-05-13 23:09:49 -06:00
|
|
|
|
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);
|
2015-05-15 15:25:09 -06:00
|
|
|
|
var provider = BlockRepository.GetBlockProvider(id);
|
2015-05-13 23:09:49 -06:00
|
|
|
|
if (id != 0)
|
2015-05-15 15:25:09 -06:00
|
|
|
|
drawableCoordinates.Add(coords);
|
|
|
|
|
if (!provider.Opaque)
|
2015-05-13 23:09:49 -06:00
|
|
|
|
{
|
2015-05-15 15:25:09 -06:00
|
|
|
|
// Add adjacent blocks
|
|
|
|
|
foreach (var a in AdjacentCoordinates)
|
2015-05-14 18:47:16 -06:00
|
|
|
|
{
|
2015-05-15 15:25:09 -06:00
|
|
|
|
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);
|
2015-05-14 18:47:16 -06:00
|
|
|
|
}
|
2015-05-13 23:09:49 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-05-15 15:25:09 -06:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-05-15 15:48:20 -06:00
|
|
|
|
var meshes = new Tuple<Mesh, Mesh>(
|
|
|
|
|
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;
|
2015-05-15 15:25:09 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-05-15 15:48:20 -06:00
|
|
|
|
}
|