You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

127 lines
4.5 KiB

2 years ago
using System;
using gaemstone.Client;
using gaemstone.ECS;
using Silk.NET.Maths;
using static gaemstone.Bloxel.Components.CoreComponents;
using static gaemstone.Bloxel.Systems.BasicWorldGenerator;
using static gaemstone.Client.Components.RenderingComponents;
2 years ago
namespace gaemstone.Bloxel.Client.Systems;
2 years ago
[Module]
public class ChunkMeshGenerator
{
private const int StartingCapacity = 1024;
private static readonly Vector3D<float>[][] OffsetPerFacing = {
new Vector3D<float>[]{ new(1,1,1), new(1,0,1), new(1,0,0), new(1,1,0) }, // East (+X)
new Vector3D<float>[]{ new(0,1,0), new(0,0,0), new(0,0,1), new(0,1,1) }, // West (-X)
new Vector3D<float>[]{ new(1,1,0), new(0,1,0), new(0,1,1), new(1,1,1) }, // Up (+Y)
new Vector3D<float>[]{ new(1,0,1), new(0,0,1), new(0,0,0), new(1,0,0) }, // Down (-Y)
new Vector3D<float>[]{ new(0,1,1), new(0,0,1), new(1,0,1), new(1,1,1) }, // South (+Z)
new Vector3D<float>[]{ new(1,1,0), new(1,0,0), new(0,0,0), new(0,1,0) } // North (-Z)
};
private static readonly int[] TriangleIndices
= { 0, 1, 3, 1, 2, 3 };
private ushort[] _indices = new ushort[StartingCapacity];
private Vector3D<float>[] _vertices = new Vector3D<float>[StartingCapacity];
private Vector3D<float>[] _normals = new Vector3D<float>[StartingCapacity];
private Vector2D<float>[] _uvs = new Vector2D<float>[StartingCapacity];
[System]
public void GenerateChunkMeshes(Universe universe, EntityRef entity,
in Chunk chunk, ChunkStoreBlocks blocks,
2 years ago
HasBasicWorldGeneration _1, [Not] Mesh _2)
{
var maybeMesh = Generate(universe, chunk.Position, blocks);
if (maybeMesh is Mesh mesh) entity.Set(mesh);
2 years ago
else entity.Delete();
}
public Mesh? Generate(Universe universe, ChunkPos chunkPos,
ChunkStoreBlocks centerBlocks)
2 years ago
{
// TODO: We'll need a way to get neighbors again.
// var storages = new ChunkStoreBlocks[3, 3, 3];
2 years ago
// foreach (var (x, y, z) in Neighbors.ALL.Prepend(Neighbor.None))
// if (_chunkStore.TryGetEntityID(chunkPos.Add(x, y, z), out var neighborID))
// if (_storageStore.TryGet(neighborID, out var storage))
// storages[x+1, y+1, z+1] = storage;
// var centerStorage = storages[1, 1, 1];
var storages = new ChunkStoreBlocks[3, 3, 3];
storages[1, 1, 1] = centerBlocks;
2 years ago
var indexCount = 0;
var vertexCount = 0;
for (var x = 0; x < 16; x++)
for (var y = 0; y < 16; y++)
for (var z = 0; z < 16; z++) {
var blockEntity = new Entity(centerBlocks[x, y, z]);
if (!blockEntity.IsValid) continue;
var block = new EntityRef(universe, blockEntity);
2 years ago
var blockVertex = new Vector3D<float>(x, y, z);
var textureCell = block.Get<TextureCoords4>();
foreach (var facing in BlockFacings.All) {
if (!IsNeighborEmpty(storages, x, y, z, facing)) continue;
if (_indices.Length <= indexCount + 6)
Array.Resize(ref _indices, _indices.Length << 1);
if (_vertices.Length <= vertexCount + 4) {
Array.Resize(ref _vertices, _vertices.Length << 1);
Array.Resize(ref _normals , _vertices.Length << 1);
Array.Resize(ref _uvs , _vertices.Length << 1);
}
for (var i = 0; i < TriangleIndices.Length; i++)
_indices[indexCount++] = (ushort)(vertexCount + TriangleIndices[i]);
var normal = facing.ToVector3();
for (var i = 0; i < 4; i++) {
var offset = OffsetPerFacing[(int)facing][i];
_vertices[vertexCount] = blockVertex + offset;
_normals[vertexCount] = normal;
_uvs[vertexCount] = i switch {
0 => textureCell.TopLeft,
1 => textureCell.BottomLeft,
2 => textureCell.BottomRight,
3 => textureCell.TopRight,
_ => throw new InvalidOperationException()
};
vertexCount++;
}
}
}
return (indexCount > 0)
? MeshManager.Create(universe,
_indices.AsSpan(0, indexCount), _vertices.AsSpan(0, vertexCount),
_normals.AsSpan(0, vertexCount), _uvs.AsSpan(0, vertexCount))
: null;
}
static bool IsNeighborEmpty(
ChunkStoreBlocks[,,] blocks,
2 years ago
int x, int y, int z, BlockFacing facing)
{
var cx = 1; var cy = 1; var cz = 1;
switch (facing) {
case BlockFacing.East : x += 1; if (x >= 16) cx += 1; break;
case BlockFacing.West : x -= 1; if (x < 0) cx -= 1; break;
case BlockFacing.Up : y += 1; if (y >= 16) cy += 1; break;
case BlockFacing.Down : y -= 1; if (y < 0) cy -= 1; break;
case BlockFacing.South : z += 1; if (z >= 16) cz += 1; break;
case BlockFacing.North : z -= 1; if (z < 0) cz -= 1; break;
}
var neighborChunk = blocks[cx, cy, cz];
2 years ago
if (neighborChunk == null) return true;
var neighborBlock = neighborChunk[x & 0b1111, y & 0b1111, z & 0b1111];
return !neighborBlock.IsValid;
2 years ago
}
}