Files
voxel/World.cs
2025-09-22 10:56:55 +02:00

187 lines
6.3 KiB
C#

using OpenTK.Mathematics;
using System.Collections.Generic;
using System.Drawing;
namespace Voxel
{
public class World
{
private Dictionary<(int, int), Chunk> _chunks;
public World()
{
_chunks = new Dictionary<(int, int), Chunk>();
}
public Chunk GetChunk(int chunkX, int chunkZ)
{
_chunks.TryGetValue((chunkX, chunkZ), out Chunk chunk);
return chunk;
}
public void AddChunk(Chunk chunk)
{
_chunks[(chunk.X, chunk.Y)] = chunk;
Dictionary<Orientation, Orientation> oppositeOrientation = new()
{
{Orientation.West, Orientation.East},
{Orientation.East, Orientation.West},
{Orientation.North, Orientation.South},
{Orientation.South, Orientation.North},
};
foreach (var (orientation, neighbor) in GetChunkNeighbors(chunk))
{
neighbor.Neighbors[oppositeOrientation[orientation]] = chunk;
chunk.Neighbors[orientation] = neighbor;
neighbor.UpdateChunkMesh();
}
chunk.UpdateChunkMesh();
}
public void RemoveChunk(int chunkX, int chunkZ)
{
_chunks.Remove((chunkX, chunkZ));
}
public IEnumerable<Chunk> GetAllChunks()
{
return _chunks.Values;
}
public Blocks GetBlock(int worldX, int worldY, int worldZ)
{
int chunkX = worldX / Chunk.Size;
int chunkZ = worldZ / Chunk.Size;
Chunk chunk = GetChunk(chunkX, chunkZ);
if (chunk == null) return 0; // air if chunk not loaded
int localX = worldX % Chunk.Size;
int localZ = worldZ % Chunk.Size;
return chunk.GetBlock(localX, worldY, localZ);
}
List<Orientation> GetEdgeOrientations(int localX, int localZ, int size)
{
var orientations = new List<Orientation>();
if (localX == 0) orientations.Add(Orientation.West);
if (localX == size - 1) orientations.Add(Orientation.East);
if (localZ == size - 1) orientations.Add(Orientation.South); // assuming Z+ is North
if (localZ == 0) orientations.Add(Orientation.North);
return orientations;
}
public void SetBlock(int worldX, int worldY, int worldZ, Blocks block)
{
int chunkX = worldX / Chunk.Size;
int chunkZ = worldZ / Chunk.Size;
Chunk chunk = GetChunk(chunkX, chunkZ);
if (chunk == null) return;
int localX = worldX % Chunk.Size;
int localZ = worldZ % Chunk.Size;
chunk.SetBlock(localX, worldY, localZ, block);
if (block == Blocks.Air && (localX == 15 || localX == 0) || (localZ == 15 || localZ == 0))
{
foreach (var orientation in GetEdgeOrientations(localX, localZ, Chunk.Size))
{
if (chunk.Neighbors.TryGetValue(orientation, out var neighbor) && neighbor != null)
{
neighbor.UpdateChunkMesh();
Console.WriteLine($"Updated neighbor at {orientation}");
}
}
}
}
public IEnumerable<(Orientation orientation, Chunk neighbor)> GetChunkNeighbors(Chunk chunk)
{
Dictionary<Orientation, (int x, int y)> offsets = new()
{
{ Orientation.West, (1, 0) },
{ Orientation.East, (-1, 0) },
{ Orientation.North, (0, 1) },
{ Orientation.South, (0, -1) }
};
foreach (var kv in offsets)
{
int nx = chunk.X + kv.Value.x;
int ny = chunk.Y + kv.Value.y;
Chunk neighbor = GetChunk(nx, ny);
if (neighbor != null)
yield return (kv.Key, neighbor);
}
}
public (bool success, Blocks block, int x, int y, int z, Vector3i normal) Raycast(Vector3 origin, Vector3 direction, float maxDistance)
{
int x = (int)MathF.Floor(origin.X);
int y = (int)MathF.Floor(origin.Y);
int z = (int)MathF.Floor(origin.Z);
int stepX = direction.X > 0 ? 1 : -1;
int stepY = direction.Y > 0 ? 1 : -1;
int stepZ = direction.Z > 0 ? 1 : -1;
float tDeltaX = direction.X != 0 ? MathF.Abs(1 / direction.X) : float.MaxValue;
float tDeltaY = direction.Y != 0 ? MathF.Abs(1 / direction.Y) : float.MaxValue;
float tDeltaZ = direction.Z != 0 ? MathF.Abs(1 / direction.Z) : float.MaxValue;
float tMaxX = direction.X > 0 ? (MathF.Floor(origin.X) + 1 - origin.X) * tDeltaX
: (origin.X - MathF.Floor(origin.X)) * tDeltaX;
float tMaxY = direction.Y > 0 ? (MathF.Floor(origin.Y) + 1 - origin.Y) * tDeltaY
: (origin.Y - MathF.Floor(origin.Y)) * tDeltaY;
float tMaxZ = direction.Z > 0 ? (MathF.Floor(origin.Z) + 1 - origin.Z) * tDeltaZ
: (origin.Z - MathF.Floor(origin.Z)) * tDeltaZ;
float distance = 0f;
Vector3i normal = Vector3i.Zero;
while (distance <= maxDistance)
{
Blocks block = GetBlock(x, y, z);
if (block != Blocks.Air)
return (true, block, x, y, z, normal);
if (tMaxX < tMaxY && tMaxX < tMaxZ)
{
x += stepX;
normal = new Vector3i(-stepX, 0, 0);
distance = tMaxX;
tMaxX += tDeltaX;
}
else if (tMaxY < tMaxZ)
{
y += stepY;
normal = new Vector3i(0, -stepY, 0);
distance = tMaxY;
tMaxY += tDeltaY;
}
else
{
z += stepZ;
normal = new Vector3i(0, 0, -stepZ);
distance = tMaxZ;
tMaxZ += tDeltaZ;
}
}
return (false, Blocks.Air, 0, 0, 0, Vector3i.Zero);
}
public void Update()
{
}
}
}