Created
April 24, 2020 00:38
-
-
Save himanshugoel2797/449e4282dfa7ffe69c0f5f5f76a2b300 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Kokoro.Math; | |
using System; | |
using System.Collections.Concurrent; | |
using System.Collections.Generic; | |
using System.Runtime.CompilerServices; | |
using System.Text; | |
using System.Threading; | |
using System.Threading.Tasks; | |
namespace Kokoro.Graphics.Voxel | |
{ | |
public class VoxelMesher | |
{ | |
class MeshOp | |
{ | |
public SemaphoreSlim Signal { get; set; } | |
public VoxelCacheEntry CacheIdx { get; set; } | |
} | |
private ConcurrentQueue<MeshOp> PendingMeshOps; | |
private ConcurrentQueue<MeshOp> BacklogOps; | |
private Thread[] MeshingThreads; | |
private SemaphoreSlim PendingMeshSync; | |
public VoxelWorld Parent { get; private set; } | |
public VoxelMesher(VoxelWorld world) | |
{ | |
Parent = world; | |
PendingMeshOps = new ConcurrentQueue<MeshOp>(); | |
BacklogOps = new ConcurrentQueue<MeshOp>(); | |
PendingMeshSync = new SemaphoreSlim(0); | |
MeshingThreads = new Thread[Environment.ProcessorCount - VoxelWorld.IOThreads - 1]; //leave threads for streaming and culling | |
for (int i = 0; i < MeshingThreads.Length; i++) | |
{ | |
MeshingThreads[i] = new Thread(MeshOpThread); | |
MeshingThreads[i].Start(i); | |
} | |
} | |
//Receive a queue of things to mesh, meshing distributed across multiple threads | |
private void MeshOpThread(object arg) | |
{ | |
int tidx = (int)arg; | |
while (true) | |
{ | |
PendingMeshSync.Wait(); | |
if (PendingMeshOps.TryDequeue(out var op)) | |
{ | |
var ent = op.CacheIdx; | |
if (ent.CacheID != null) //Only remesh if the node is still allocated | |
{ | |
//Parent.Cache.UpdateEntry(ent).ConfigureAwait(false); | |
RebuildFullMesh(ent, ent.Offset); | |
} | |
op.Signal.Release(); | |
} | |
else | |
PendingMeshSync.Release(); //Failed to acquire job, prepare to try again | |
while (PendingMeshOps.Count < VoxelWorld.MaxMeshJobs - 10 && BacklogOps.Count > 0) | |
if (BacklogOps.TryDequeue(out var backlog)) | |
PendingMeshOps.Enqueue(backlog); | |
} | |
} | |
public void MeshObject(VoxelCacheEntry idx) | |
{ | |
idx.RemeshingStatus.Wait(); | |
var op = new MeshOp() | |
{ | |
Signal = idx.RemeshingStatus, | |
CacheIdx = idx, | |
}; | |
if (PendingMeshOps.Count > VoxelWorld.MaxMeshJobs) | |
if (PendingMeshOps.TryDequeue(out var backlog)) | |
BacklogOps.Enqueue(backlog); | |
PendingMeshOps.Enqueue(op); | |
PendingMeshSync.Release(); | |
} | |
#region Meshing | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private static int GetIndex(int x, int y, int z) | |
{ | |
//int idx = 0; | |
//for (int i = 0; i < VoxelWorld.SideLog; i++) | |
//{ | |
// idx |= ((x >> i) & 1) << (i * 3 + 0); | |
// idx |= ((y >> i) & 1) << (i * 3 + 1); | |
// idx |= ((z >> i) & 1) << (i * 3 + 2); | |
//} | |
//return idx; | |
x += 1; | |
y += 1; | |
z += 1; | |
return x * ((VoxelWorld.Side + 2) * (VoxelWorld.Side + 2)) + y * (VoxelWorld.Side + 2) + z; | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private static int GetIndex(int x, int y) | |
{ | |
return (y & (VoxelWorld.Side - 1)) << VoxelWorld.SideLog | (x & (VoxelWorld.Side - 1)); | |
} | |
public void RebuildFullMesh(VoxelCacheEntry entry, Vector3 offset) | |
{ | |
unsafe | |
{ | |
entry.VoxelCount = 0; | |
var data = entry.Voxels; | |
//System.Diagnostics.Stopwatch stopwatch = System.Diagnostics.Stopwatch.StartNew(); | |
var indices = new List<uint>(VoxelWorld.Side * VoxelWorld.Side * VoxelWorld.Side * 6); | |
var boundingAABB = new List<BoundingBox>((VoxelWorld.Side * VoxelWorld.Side * VoxelWorld.Side) / VoxelWorld.BlockSize + 1); | |
var boundingSphere = new List<Vector4>((VoxelWorld.Side * VoxelWorld.Side * VoxelWorld.Side) / VoxelWorld.BlockSize + 1); | |
ushort blk_idx_cnt = 0; | |
var min = stackalloc byte[3]; | |
var max = stackalloc byte[3]; | |
var tmp = stackalloc byte[6 * 3]; | |
for (int i = 0; i < 3; i++) | |
{ | |
min[i] = byte.MaxValue; | |
max[i] = byte.MinValue; | |
} | |
void computeBounds() | |
{ | |
if (blk_idx_cnt != 0) | |
{ | |
//while (blk_idx_cnt % 3 != 0) | |
//{ | |
// indices.Add(indices[indices.Count - 1]); | |
// blk_idx_cnt++; | |
//} | |
for (int i = 0; i < 3; i++) | |
if (min[i] == max[i]) | |
{ | |
if (min[i] == byte.MinValue) //Can't decrement min | |
max[i]++; | |
else if (max[i] == byte.MaxValue) //Can't increment max | |
min[i]--; | |
else | |
max[i]++; //Default to incrementing max | |
} | |
Vector3 minv = new Vector3(min[0], min[1], min[2]) + offset; | |
Vector3 maxv = new Vector3(max[0], max[1], max[2]) + offset; | |
Vector3 c = (minv + maxv) * 0.5f; | |
float rad = (maxv - c).Length; | |
boundingAABB.Add(new BoundingBox(minv, maxv)); | |
boundingSphere.Add(new Vector4(c, rad)); | |
blk_idx_cnt = 0; | |
for (int i = 0; i < 3; i++) | |
{ | |
min[i] = byte.MaxValue; | |
max[i] = byte.MinValue; | |
} | |
} | |
} | |
void addFace(byte* tmp, byte cur) | |
{ | |
for (int i = 0; i < 6; i++) | |
{ | |
var vec = tmp[i * 3] << (VoxelWorld.SideLog * 2 + 2) | tmp[i * 3 + 1] << (VoxelWorld.SideLog + 1) | tmp[i * 3 + 2]; | |
min[0] = System.Math.Min(min[0], tmp[i * 3]); | |
min[1] = System.Math.Min(min[1], tmp[i * 3 + 1]); | |
min[2] = System.Math.Min(min[2], tmp[i * 3 + 2]); | |
max[0] = System.Math.Max(max[0], tmp[i * 3]); | |
max[1] = System.Math.Max(max[1], tmp[i * 3 + 1]); | |
max[2] = System.Math.Max(max[2], tmp[i * 3 + 2]); | |
blk_idx_cnt++; | |
indices.Add((uint)cur << 18 | (uint)(vec & 0x3ffff)); | |
} | |
if (blk_idx_cnt == VoxelWorld.BlockSize) | |
{ | |
computeBounds(); | |
} | |
} | |
void addPackedFace(byte x, byte y, byte z, byte dir, byte cur) | |
{ | |
min[0] = System.Math.Min(min[0], x); | |
min[1] = System.Math.Min(min[1], y); | |
min[2] = System.Math.Min(min[2], z); | |
max[0] = System.Math.Max(max[0], (byte)(x + 1)); | |
max[1] = System.Math.Max(max[1], (byte)(y + 1)); | |
max[2] = System.Math.Max(max[2], (byte)(z + 1)); | |
var vec = x << (VoxelWorld.SideLog * 2 + 2) | y << (VoxelWorld.SideLog + 1) | z; | |
blk_idx_cnt++; | |
indices.Add((uint)cur << 24 | (uint)dir << 18 | (uint)(vec & 0x3ffff)); | |
if (blk_idx_cnt == VoxelWorld.BlockSize) | |
computeBounds(); | |
} | |
entry.VoxelLock.EnterWriteLock(); | |
try | |
{ | |
if (!entry.VoxelDirty) | |
return; //No changes to mesh | |
//ComputeVisibility(data, vis, entry.NeighborFaces); | |
//for each height value along each axis, extract faces | |
//assign bit value to each face, then emit faces when doing localized iteration | |
for (int j = 0; j < data.Length; j++) | |
{ | |
int xi, yi, zi; | |
xi = (j & 0x1) | ((j & 0x8) >> 2) | ((j & 0x40) >> 4) | ((j & 0x200) >> 6) | ((j & 0x1000) >> 8); | |
yi = (j & 0x2) >> 1 | ((j & 0x10) >> 3) | ((j & 0x80) >> 5) | ((j & 0x400) >> 7) | ((j & 0x2000) >> 9); | |
zi = (j & 0x4) >> 2 | ((j & 0x20) >> 4) | ((j & 0x100) >> 6) | ((j & 0x800) >> 8) | ((j & 0x4000) >> 10); | |
//TODO: store voxel data in the above order | |
byte x = (byte)xi; | |
byte y = (byte)yi; | |
byte z = (byte)zi; | |
//var v = vis[GetIndex(x, y, z)]; | |
var cur = data[GetIndex(x, y, z)]; | |
var top = data[GetIndex(x, y - 1, z)]; | |
var btm = data[GetIndex(x, y + 1, z)]; | |
var frt = data[GetIndex(x, y, z - 1)]; | |
var bck = data[GetIndex(x, y, z + 1)]; | |
var lft = data[GetIndex(x - 1, y, z)]; | |
var rgt = data[GetIndex(x + 1, y, z)]; | |
var v = 0; | |
if (top == 0) | |
v |= (1 << 3); | |
if (btm == 0) | |
v |= (1 << 4); | |
if (frt == 0) | |
v |= (1 << 5); | |
if (bck == 0) | |
v |= (1 << 6); | |
if (lft == 0) | |
v |= (1 << 1); | |
if (rgt == 0) | |
v |= (1 << 2); | |
if (cur == 0) continue; | |
if ((v & (1 << 3)) != 0) | |
{ | |
//0 | |
var vcntr = 0; | |
tmp[vcntr++] = (byte)(x + 1); | |
tmp[vcntr++] = y; | |
tmp[vcntr++] = z; | |
tmp[vcntr++] = x; | |
tmp[vcntr++] = y; | |
tmp[vcntr++] = (byte)(z + 1); | |
tmp[vcntr++] = x; | |
tmp[vcntr++] = y; | |
tmp[vcntr++] = z; | |
tmp[vcntr++] = (byte)(x + 1); | |
tmp[vcntr++] = y; | |
tmp[vcntr++] = z; | |
tmp[vcntr++] = (byte)(x + 1); | |
tmp[vcntr++] = y; | |
tmp[vcntr++] = (byte)(z + 1); | |
tmp[vcntr++] = x; | |
tmp[vcntr++] = y; | |
tmp[vcntr++] = (byte)(z + 1); | |
addFace(tmp, cur); | |
//addPackedFace(x, y, z, (1 << 4) | (2 << 2) | 0, cur); | |
} | |
if ((v & (1 << 4)) != 0) | |
{ | |
//1 | |
var vcntr = 0; | |
tmp[vcntr++] = x; | |
tmp[vcntr++] = (byte)(y + 1); | |
tmp[vcntr++] = z; | |
tmp[vcntr++] = x; | |
tmp[vcntr++] = (byte)(y + 1); | |
tmp[vcntr++] = (byte)(z + 1); | |
tmp[vcntr++] = (byte)(x + 1); | |
tmp[vcntr++] = (byte)(y + 1); | |
tmp[vcntr++] = z; | |
tmp[vcntr++] = x; | |
tmp[vcntr++] = (byte)(y + 1); | |
tmp[vcntr++] = (byte)(z + 1); | |
tmp[vcntr++] = (byte)(x + 1); | |
tmp[vcntr++] = (byte)(y + 1); | |
tmp[vcntr++] = (byte)(z + 1); | |
tmp[vcntr++] = (byte)(x + 1); | |
tmp[vcntr++] = (byte)(y + 1); | |
tmp[vcntr++] = z; | |
addFace(tmp, cur); | |
//addPackedFace(x, (byte)(y + 1), z, (2 << 4) | (2 << 2) | 0, cur); | |
} | |
if ((v & (1 << 1)) != 0) | |
{ | |
//2 | |
var vcntr = 0; | |
tmp[vcntr++] = x; | |
tmp[vcntr++] = y; | |
tmp[vcntr++] = z; | |
tmp[vcntr++] = x; | |
tmp[vcntr++] = y; | |
tmp[vcntr++] = (byte)(z + 1); | |
tmp[vcntr++] = x; | |
tmp[vcntr++] = (byte)(y + 1); | |
tmp[vcntr++] = z; | |
tmp[vcntr++] = x; | |
tmp[vcntr++] = y; | |
tmp[vcntr++] = (byte)(z + 1); | |
tmp[vcntr++] = x; | |
tmp[vcntr++] = (byte)(y + 1); | |
tmp[vcntr++] = (byte)(z + 1); | |
tmp[vcntr++] = x; | |
tmp[vcntr++] = (byte)(y + 1); | |
tmp[vcntr++] = z; | |
addFace(tmp, cur); | |
//addPackedFace(x, y, z, (2 << 4) | (2 << 2) | 1, cur); | |
} | |
if ((v & (1 << 2)) != 0) | |
{ | |
//3 | |
var vcntr = 0; | |
tmp[vcntr++] = (byte)(x + 1); | |
tmp[vcntr++] = y; | |
tmp[vcntr++] = z; | |
tmp[vcntr++] = (byte)(x + 1); | |
tmp[vcntr++] = (byte)(y + 1); | |
tmp[vcntr++] = z; | |
tmp[vcntr++] = (byte)(x + 1); | |
tmp[vcntr++] = y; | |
tmp[vcntr++] = (byte)(z + 1); | |
tmp[vcntr++] = (byte)(x + 1); | |
tmp[vcntr++] = (byte)(y + 1); | |
tmp[vcntr++] = z; | |
tmp[vcntr++] = (byte)(x + 1); | |
tmp[vcntr++] = (byte)(y + 1); | |
tmp[vcntr++] = (byte)(z + 1); | |
tmp[vcntr++] = (byte)(x + 1); | |
tmp[vcntr++] = y; | |
tmp[vcntr++] = (byte)(z + 1); | |
addFace(tmp, cur); | |
//addPackedFace((byte)(x + 1), y, z, (1 << 4) | (2 << 2) | 1, cur); | |
} | |
if ((v & (1 << 5)) != 0) | |
{ | |
//4 | |
var vcntr = 0; | |
tmp[vcntr++] = x; | |
tmp[vcntr++] = y; | |
tmp[vcntr++] = z; | |
tmp[vcntr++] = x; | |
tmp[vcntr++] = (byte)(y + 1); | |
tmp[vcntr++] = z; | |
tmp[vcntr++] = (byte)(x + 1); | |
tmp[vcntr++] = y; | |
tmp[vcntr++] = z; | |
tmp[vcntr++] = x; | |
tmp[vcntr++] = (byte)(y + 1); | |
tmp[vcntr++] = z; | |
tmp[vcntr++] = (byte)(x + 1); | |
tmp[vcntr++] = (byte)(y + 1); | |
tmp[vcntr++] = z; | |
tmp[vcntr++] = (byte)(x + 1); | |
tmp[vcntr++] = y; | |
tmp[vcntr++] = z; | |
addFace(tmp, cur); | |
//addPackedFace(x, y, z, (2 << 4) | (1 << 2) | 0, cur); | |
} | |
if ((v & (1 << 6)) != 0) | |
{ | |
//5 | |
var vcntr = 0; | |
tmp[vcntr++] = x; | |
tmp[vcntr++] = y; | |
tmp[vcntr++] = (byte)(z + 1); | |
tmp[vcntr++] = (byte)(x + 1); | |
tmp[vcntr++] = y; | |
tmp[vcntr++] = (byte)(z + 1); | |
tmp[vcntr++] = x; | |
tmp[vcntr++] = (byte)(y + 1); | |
tmp[vcntr++] = (byte)(z + 1); | |
tmp[vcntr++] = (byte)(x + 1); | |
tmp[vcntr++] = y; | |
tmp[vcntr++] = (byte)(z + 1); | |
tmp[vcntr++] = (byte)(x + 1); | |
tmp[vcntr++] = (byte)(y + 1); | |
tmp[vcntr++] = (byte)(z + 1); | |
tmp[vcntr++] = x; | |
tmp[vcntr++] = (byte)(y + 1); | |
tmp[vcntr++] = (byte)(z + 1); | |
addFace(tmp, cur); | |
//addPackedFace(x, y, (byte)(z + 1), (1 << 4) | (1 << 2) | 0, cur); | |
} | |
entry.VoxelCount++; | |
} | |
computeBounds(); | |
//Remeshing is done, so voxel data is no longer dirty | |
entry.VoxelDirty = false; | |
entry.IsEmpty = indices.Count == 0; | |
} | |
finally | |
{ | |
entry.VoxelLock.ExitWriteLock(); | |
} | |
entry.MeshLock.EnterWriteLock(); | |
try | |
{ | |
entry.Indices = indices.ToArray(); | |
entry.BoundingSpheres = boundingSphere.ToArray(); | |
entry.BoundingAABBs = boundingAABB.ToArray(); | |
entry.MeshDirty = true; //Mesh data needs to be reuploaded | |
} | |
finally | |
{ | |
entry.MeshLock.ExitWriteLock(); | |
} | |
//stopwatch.Stop(); | |
//Console.WriteLine($"[Index Count: {indices.Count}, Meshing time: {stopwatch.Elapsed.TotalMilliseconds}ms"); | |
} | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment