Archived
Private
Public Access
1
0
This repository has been archived on 2026-02-04. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
2022-11-12 13:10:03 +01:00

298 lines
11 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using Utils;
namespace Terrain {
public class Chunk {
public Vector2Int Position;
public Vector3 WorldPosition;
private GameObject _object;
private MeshRenderer _renderer;
private MeshFilter _filter;
private World _world;
private bool _active;
private int _vertexIndex;
private readonly List<Vector3> _vertices = new List<Vector3>();
private readonly List<int> _triangles = new List<int>();
private readonly List<int> _transparentTirangles = new List<int>();
private readonly List<Vector2> _uvs = new List<Vector2>();
private readonly List<Color> _colors = new List<Color>();
private readonly List<Vector3> _normals = new List<Vector3>();
public readonly BlockState[,,] BlockMap = new BlockState[WorldData.ChunkSize.x, WorldData.ChunkSize.y, WorldData.ChunkSize.x];
public readonly Queue<BlockMod> Modifications = new Queue<BlockMod>();
public Chunk(in World world, in Vector2Int position) {
Position = position;
_world = world;
}
public void Generate() {
_object = new GameObject();
_filter = _object.AddComponent<MeshFilter>();
_renderer = _object.AddComponent<MeshRenderer>();
_renderer.materials = new[] { _world.material, _world.transparent };
//_renderer.material = _world.material;
_object.transform.SetParent(_world.transform);
_object.transform.position = new Vector3(Position.x * WorldData.ChunkSize.x, 0.0f,
Position.y * WorldData.ChunkSize.x);
_object.name = $"Chunk [{Position.x}:{Position.y}]";
WorldPosition = _object.transform.position;
PopulateBlockMap();
}
public bool Active {
get => _active;
set {
_active = value;
if (_object != null)
_object.SetActive(value);
}
}
public bool Generated { get; private set; }
public bool Editable => Generated;
public void Update() {
while (Modifications.Count > 0) {
BlockMod mod = Modifications.Dequeue();
int x = Mathf.FloorToInt(mod.Position.x - WorldPosition.x);
int y = Mathf.FloorToInt(mod.Position.y);
int z = Mathf.FloorToInt(mod.Position.z - WorldPosition.z);
BlockMap[x, y, z].Block = mod.Block;
}
ClearMeshData();
CalculateLight();
for (int y = 0; y < WorldData.ChunkSize.y; y++) {
for (int x = 0; x < WorldData.ChunkSize.x; x++) {
for (int z = 0; z < WorldData.ChunkSize.x; z++) {
if (_world.blockTypes[BlockMap[x, y, z].Block].solid)
UpdateMeshData(new Vector3(x, y, z));
}
}
}
lock (_world.ChunksToDraw) {
_world.ChunksToDraw.Enqueue(this);
}
}
private void CalculateLight() {
Queue<Vector3Int> litVoxels = new Queue<Vector3Int>();
for (int x = 0; x < WorldData.ChunkSize.x; x++) {
for (int z = 0; z < WorldData.ChunkSize.x; z++) {
float lightRay = 1.0f;
for (int y = WorldData.ChunkSize.y - 1; y >= 0; y--) {
BlockState state = BlockMap[x, y, z];
if (state.Block > 0 && _world.blockTypes[state.Block].transparency < lightRay)
lightRay = _world.blockTypes[state.Block].transparency;
state.GlobalLightPercent = lightRay;
BlockMap[x, y, z] = state;
if (lightRay > WorldData.LightFalloff)
litVoxels.Enqueue(new Vector3Int(x, y, z));
}
}
}
while (litVoxels.Count > 0) {
Vector3Int pos = litVoxels.Dequeue();
for (int i = 0; i < 6; i++) {
Vector3Int block = new Vector3Int((int)(pos.x + WorldData.FaceChecks[i].x), (int)(pos.y + WorldData.FaceChecks[i].y), (int)(pos.z + WorldData.FaceChecks[i].z));
if (IsBlockInChunk(block)) {
BlockState state = BlockMap[block.x, block.y, block.z];
if (state.GlobalLightPercent < BlockMap[pos.x, pos.y, pos.z].GlobalLightPercent - WorldData.LightFalloff) {
state.GlobalLightPercent = BlockMap[pos.x, pos.y, pos.z].GlobalLightPercent - WorldData.LightFalloff;
if (state.GlobalLightPercent > WorldData.LightFalloff)
litVoxels.Enqueue(block);
BlockMap[block.x, block.y, block.z] = state;
}
}
}
}
}
public BlockState GetBlockFromWorldPosition(in Vector3 pos) {
int x = Mathf.FloorToInt(pos.x - WorldPosition.x);
int y = Mathf.FloorToInt(pos.y);
int z = Mathf.FloorToInt(pos.z - WorldPosition.z);
return BlockMap[x, y, z];
}
private void PopulateBlockMap() {
for (int y = 0; y < WorldData.ChunkSize.y; y++) {
for (int x = 0; x < WorldData.ChunkSize.x; x++) {
for (int z = 0; z < WorldData.ChunkSize.x; z++) {
BlockMap[x, y, z] = new BlockState(_world.GenerateBlock(new Vector3(x, y, z) + WorldPosition));
}
}
}
lock (_world.ChunkUpdateThreadLock) {
_world.ChunksToUpdate.Add(this);
}
Generated = true;
if (World.Settings.enableChunkLoadingAnimation)
_object.AddComponent<ChunkLoadAnimation>();
}
public void EditBlock(in Vector3 pos, in byte block) {
int x = Mathf.FloorToInt(pos.x - WorldPosition.x);
int y = Mathf.FloorToInt(pos.y);
int z = Mathf.FloorToInt(pos.z - WorldPosition.z);
BlockMap[x, y, z].Block = block;
lock (_world.ChunkUpdateThreadLock) {
_world.ChunksToUpdate.Insert(0, this);
UpdateSurroundingBlocks(new Vector3(x, y, z));
}
}
private void UpdateSurroundingBlocks(in Vector3 block) {
for (int i = 0; i < 6; i++) {
Vector3 currentBlock = block + WorldData.FaceChecks[i];
if (!IsBlockInChunk(currentBlock))
_world.ChunksToUpdate.Insert(0, _world.GetChunkFromWorldPosition(currentBlock + WorldPosition));
}
}
private void ClearMeshData() {
_vertexIndex = 0;
_vertices.Clear();
_triangles.Clear();
_transparentTirangles.Clear();
_uvs.Clear();
_colors.Clear();
_normals.Clear();
}
private bool IsBlockInChunk(in Vector3 block) {
return !(block.x < 0 || block.x > WorldData.ChunkSize.x - 1 ||
block.y < 0 || block.y > WorldData.ChunkSize.y - 1 ||
block.z < 0 || block.z > WorldData.ChunkSize.x - 1);
}
private BlockState CheckBlock(in Vector3 pos) {
int x = Mathf.FloorToInt(pos.x);
int y = Mathf.FloorToInt(pos.y);
int z = Mathf.FloorToInt(pos.z);
if (!IsBlockInChunk(new Vector3(x, y, z)))
return _world.GetBlock(pos + WorldPosition);
return BlockMap[x, y, z];
}
private void UpdateMeshData(in Vector3 pos) {
int x = Mathf.FloorToInt(pos.x);
int y = Mathf.FloorToInt(pos.y);
int z = Mathf.FloorToInt(pos.z);
byte block = BlockMap[x, y, z].Block;
BlockType type = _world.blockTypes[block];
for (byte i = 0; i < 6; i++) {
BlockState neighbour = CheckBlock(pos + WorldData.FaceChecks[i]);
if (!_world.blockTypes[neighbour.Block].renderNeighbourFaces) continue;
_vertices.Add(pos + WorldData.BlockVerts[WorldData.BlockTris[i, 0]]);
_vertices.Add(pos + WorldData.BlockVerts[WorldData.BlockTris[i, 1]]);
_vertices.Add(pos + WorldData.BlockVerts[WorldData.BlockTris[i, 2]]);
_vertices.Add(pos + WorldData.BlockVerts[WorldData.BlockTris[i, 3]]);
for (int j = 0; j <= 3; j++) {
_normals.Add(WorldData.FaceChecks[i]);
}
AddTexture(type.GetTexture(i));
float lightLevel = neighbour.GlobalLightPercent;
_colors.Add(new Color(0, 0, 0, lightLevel));
_colors.Add(new Color(0, 0, 0, lightLevel));
_colors.Add(new Color(0, 0, 0, lightLevel));
_colors.Add(new Color(0, 0, 0, lightLevel));
if (type.renderNeighbourFaces) {
_transparentTirangles.Add(_vertexIndex);
_transparentTirangles.Add(_vertexIndex + 1);
_transparentTirangles.Add(_vertexIndex + 2);
_transparentTirangles.Add(_vertexIndex + 2);
_transparentTirangles.Add(_vertexIndex + 1);
_transparentTirangles.Add(_vertexIndex + 3);
}
else {
_triangles.Add(_vertexIndex);
_triangles.Add(_vertexIndex + 1);
_triangles.Add(_vertexIndex + 2);
_triangles.Add(_vertexIndex + 2);
_triangles.Add(_vertexIndex + 1);
_triangles.Add(_vertexIndex + 3);
}
_vertexIndex += 4;
}
}
public void CreateMesh() {
Mesh mesh = new Mesh();
mesh.vertices = _vertices.ToArray();
mesh.subMeshCount = 2;
mesh.SetTriangles(_triangles.ToArray(), 0);
mesh.SetTriangles(_transparentTirangles.ToArray(), 1);
//mesh.triangles = _triangles.ToArray();
mesh.uv = _uvs.ToArray();
mesh.colors = _colors.ToArray();
mesh.normals = _normals.ToArray();
//mesh.RecalculateNormals();
_filter.mesh = mesh;
}
private void AddTexture(in short texture) {
float y = texture / WorldData.TextureAtlasSizeInBlocks;
float x = texture % WorldData.TextureAtlasSizeInBlocks;
x *= WorldData.NormalizedBlockTextureSize;
y *= WorldData.NormalizedBlockTextureSize;
y = 1.0f - y - WorldData.NormalizedBlockTextureSize;
_uvs.Add(new Vector2(x, y));
_uvs.Add(new Vector2(x, y + WorldData.NormalizedBlockTextureSize));
_uvs.Add(new Vector2(x + WorldData.NormalizedBlockTextureSize, y));
_uvs.Add(new Vector2(x + WorldData.NormalizedBlockTextureSize, y + WorldData.NormalizedBlockTextureSize));
}
}
}