Fixed negative chunks invisible
This commit is contained in:
@@ -48,6 +48,8 @@ namespace Voxel
|
|||||||
|
|
||||||
public new void Tick()
|
public new void Tick()
|
||||||
{
|
{
|
||||||
|
_world.UpdateChunkLoading(Position);
|
||||||
|
|
||||||
previousPosition = Position;
|
previousPosition = Position;
|
||||||
|
|
||||||
float forwards = 0;
|
float forwards = 0;
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ internal class Program
|
|||||||
|
|
||||||
Console.WriteLine("Generating map...");
|
Console.WriteLine("Generating map...");
|
||||||
|
|
||||||
int worldSizeX = 8;
|
int worldSizeX = 2;
|
||||||
int worldSizeY = 8;
|
int worldSizeY = 2;
|
||||||
|
|
||||||
float maxI = worldSizeX * worldSizeY;
|
float maxI = worldSizeX * worldSizeY;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|||||||
@@ -61,11 +61,12 @@ namespace Voxel
|
|||||||
if (_world == null) return;
|
if (_world == null) return;
|
||||||
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
|
||||||
foreach (Chunk chunk in _world.GetAllChunks())
|
foreach (Chunk chunk in _world.GetAllChunks())
|
||||||
{
|
{
|
||||||
ChunkMesh chunkMesh = chunk.GetChunkMesh();
|
ChunkMesh chunkMesh = chunk.GetChunkMesh();
|
||||||
|
|
||||||
if (chunkMesh.NeedsUpdate)
|
if (chunkMesh.NeedsUpdate) // always true
|
||||||
{
|
{
|
||||||
byte[] data = chunkMesh.GetPackedData();
|
byte[] data = chunkMesh.GetPackedData();
|
||||||
GL.BufferSubData(BufferTarget.ShaderStorageBuffer, (IntPtr)offset * 8, chunkMesh.Size * 8, data);
|
GL.BufferSubData(BufferTarget.ShaderStorageBuffer, (IntPtr)offset * 8, chunkMesh.Size * 8, data);
|
||||||
|
|||||||
@@ -10,16 +10,23 @@ uniform vec3 cameraPosition;
|
|||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
float fogEnd = 512;
|
// Use squared distance
|
||||||
float fogStart = 32;
|
vec3 delta = cameraPosition - fragPos;
|
||||||
|
float distSq = dot(delta, delta);
|
||||||
float dist = length(cameraPosition - fragPos);
|
|
||||||
float fogFactor = (fogEnd - dist) / (fogEnd - fogStart);
|
// Precomputed fog parameters
|
||||||
fogFactor = clamp(fogFactor, 0, 1);
|
const float fogEndSq = 16384.0; // 128^2
|
||||||
|
const float fogStartSq = 1024.0; // 32^2
|
||||||
vec4 fogColor = vec4(0.8,0.8,0.8,1);
|
const float fogRangeInv = 1.0 / (fogEndSq - fogStartSq);
|
||||||
vec4 texColor = texture(uTexture, fragUV) * lighting;
|
|
||||||
vec4 color = mix(fogColor, texColor, fogFactor);
|
float fogFactor = (fogEndSq - distSq) * fogRangeInv;
|
||||||
|
fogFactor = clamp(fogFactor, 0.0, 1.0);
|
||||||
FragColor = vec4(color.rgb, texColor.a);
|
|
||||||
|
// Texture and lighting
|
||||||
|
vec4 texColor = texture(uTexture, fragUV);
|
||||||
|
texColor.rgb *= lighting;
|
||||||
|
|
||||||
|
// Fog blend
|
||||||
|
const vec3 fogColor = vec3(0.8, 0.8, 0.8);
|
||||||
|
FragColor = vec4(mix(fogColor, texColor.rgb, fogFactor), texColor.a);
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,7 @@ void main()
|
|||||||
uint faceIndex = gl_VertexID / 6u;
|
uint faceIndex = gl_VertexID / 6u;
|
||||||
uint vertIndex = gl_VertexID % 6u;
|
uint vertIndex = gl_VertexID % 6u;
|
||||||
|
|
||||||
uint start = faceIndex * 2u; // 2 byte per face
|
uint start = faceIndex * 2u; // 2 uint per face
|
||||||
|
|
||||||
uint u0 = faces[start]; // data in uint 0
|
uint u0 = faces[start]; // data in uint 0
|
||||||
uint u1 = faces[start + 1]; // data in uint 1
|
uint u1 = faces[start + 1]; // data in uint 1
|
||||||
@@ -95,7 +95,8 @@ void main()
|
|||||||
uint texture = u1 & 0xFFu;
|
uint texture = u1 & 0xFFu;
|
||||||
uint lightLevel = (u1 >> 8) & 0xFFu;
|
uint lightLevel = (u1 >> 8) & 0xFFu;
|
||||||
|
|
||||||
vec3 basePos = vec3(x + chunkX * 16, y, z + chunkY * 16);
|
vec3 basePos = vec3(x, y, z) + vec3(chunkX, 0, chunkY) * 16.0;
|
||||||
|
|
||||||
vec4 worldPos = vec4(basePos + offsets[facing][vertIndex], 1.0);
|
vec4 worldPos = vec4(basePos + offsets[facing][vertIndex], 1.0);
|
||||||
float light = float(lightLevel) / 255.0; // use later for caves and stuff
|
float light = float(lightLevel) / 255.0; // use later for caves and stuff
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ namespace Voxel
|
|||||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
|
||||||
|
|
||||||
CursorState = CursorState.Grabbed;
|
CursorState = CursorState.Grabbed;
|
||||||
//VSync = VSyncMode.On;
|
VSync = VSyncMode.On;
|
||||||
|
|
||||||
Camera.UpdateSize(Width, Height);
|
Camera.UpdateSize(Width, Height);
|
||||||
}
|
}
|
||||||
|
|||||||
111
World.cs
111
World.cs
@@ -8,6 +8,9 @@ namespace Voxel
|
|||||||
{
|
{
|
||||||
private Dictionary<(int, int), Chunk> _chunks;
|
private Dictionary<(int, int), Chunk> _chunks;
|
||||||
|
|
||||||
|
private (int x, int z) _lastCenter = (0, 0);
|
||||||
|
private int _loadDistance = 4;
|
||||||
|
|
||||||
private static readonly Dictionary<Orientation, (int x, int y)> _neighborOffsets = new()
|
private static readonly Dictionary<Orientation, (int x, int y)> _neighborOffsets = new()
|
||||||
{
|
{
|
||||||
{ Orientation.West, (1, 0) },
|
{ Orientation.West, (1, 0) },
|
||||||
@@ -33,6 +36,7 @@ namespace Voxel
|
|||||||
|
|
||||||
UpdateNeighboringChunks(chunk);
|
UpdateNeighboringChunks(chunk);
|
||||||
chunk.UpdateChunkMesh();
|
chunk.UpdateChunkMesh();
|
||||||
|
Renderer.MarkBuffersDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveChunk(int chunkX, int chunkZ)
|
public void RemoveChunk(int chunkX, int chunkZ)
|
||||||
@@ -52,6 +56,63 @@ namespace Voxel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public new void UpdateChunkLoading(Vector3 playerPosition)
|
||||||
|
{
|
||||||
|
int centerX = (int)Math.Floor(playerPosition.X / Chunk.Size);
|
||||||
|
int centerZ = (int)Math.Floor(playerPosition.Z / Chunk.Size);
|
||||||
|
|
||||||
|
// Quick check - skip if still in same chunk
|
||||||
|
if (centerX == _lastCenter.x && centerZ == _lastCenter.z)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_lastCenter = (centerX, centerZ);
|
||||||
|
|
||||||
|
// Calculate bounds
|
||||||
|
int minX = centerX - _loadDistance;
|
||||||
|
int maxX = centerX + _loadDistance;
|
||||||
|
int minZ = centerZ - _loadDistance;
|
||||||
|
int maxZ = centerZ + _loadDistance;
|
||||||
|
|
||||||
|
// Unload chunks outside range
|
||||||
|
UnloadDistantChunks(minX, maxX, minZ, maxZ);
|
||||||
|
|
||||||
|
// Load chunks inside range
|
||||||
|
LoadChunksInRange(minX, maxX, minZ, maxZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnloadDistantChunks(int minX, int maxX, int minZ, int maxZ)
|
||||||
|
{
|
||||||
|
var chunksToRemove = new List<(int, int)>();
|
||||||
|
|
||||||
|
foreach (var (chunkX, chunkZ) in _chunks.Keys)
|
||||||
|
{
|
||||||
|
if (chunkX < minX || chunkX > maxX || chunkZ < minZ || chunkZ > maxZ)
|
||||||
|
{
|
||||||
|
chunksToRemove.Add((chunkX, chunkZ));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var chunkPos in chunksToRemove)
|
||||||
|
{
|
||||||
|
RemoveChunk(chunkPos.Item1, chunkPos.Item2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadChunksInRange(int minX, int maxX, int minZ, int maxZ)
|
||||||
|
{
|
||||||
|
for (int x = minX; x <= maxX; x++)
|
||||||
|
{
|
||||||
|
for (int z = minZ; z <= maxZ; z++)
|
||||||
|
{
|
||||||
|
if (!_chunks.ContainsKey((x, z)))
|
||||||
|
{
|
||||||
|
Chunk chunk = new Chunk(x, z);
|
||||||
|
AddChunk(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void UpdateNeighboringChunks(Chunk chunk)
|
public void UpdateNeighboringChunks(Chunk chunk)
|
||||||
{
|
{
|
||||||
chunk.Neighbors[Orientation.West] = null;
|
chunk.Neighbors[Orientation.West] = null;
|
||||||
@@ -87,39 +148,21 @@ namespace Voxel
|
|||||||
|
|
||||||
public Blocks GetBlock(int worldX, int worldY, int worldZ)
|
public Blocks GetBlock(int worldX, int worldY, int worldZ)
|
||||||
{
|
{
|
||||||
int chunkX = worldX / Chunk.Size;
|
var (chunkX, chunkZ, localX, localZ) = WorldToChunkCoords(worldX, worldZ);
|
||||||
int chunkZ = worldZ / Chunk.Size;
|
|
||||||
Chunk chunk = GetChunk(chunkX, chunkZ);
|
Chunk chunk = GetChunk(chunkX, chunkZ);
|
||||||
if (chunk == null) return 0; // air if chunk not loaded
|
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);
|
return chunk.GetBlock(localX, worldY, localZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Orientation> GetEdgeOrientations(int localX, int localZ, int size)
|
|
||||||
{
|
|
||||||
var orientations = new List<Orientation>();
|
|
||||||
|
|
||||||
if (localX == size - 1) orientations.Add(Orientation.West);
|
|
||||||
if (localX == 0) orientations.Add(Orientation.East);
|
|
||||||
if (localZ == size - 1) orientations.Add(Orientation.North);
|
|
||||||
if (localZ == 0) orientations.Add(Orientation.South);
|
|
||||||
|
|
||||||
return orientations;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetBlock(int worldX, int worldY, int worldZ, Blocks block)
|
public void SetBlock(int worldX, int worldY, int worldZ, Blocks block)
|
||||||
{
|
{
|
||||||
int chunkX = worldX / Chunk.Size;
|
var (chunkX, chunkZ, localX, localZ) = WorldToChunkCoords(worldX, worldZ);
|
||||||
int chunkZ = worldZ / Chunk.Size;
|
|
||||||
Chunk chunk = GetChunk(chunkX, chunkZ);
|
Chunk chunk = GetChunk(chunkX, chunkZ);
|
||||||
if (chunk == null) return;
|
if (chunk == null) return;
|
||||||
|
|
||||||
int localX = worldX % Chunk.Size;
|
|
||||||
int localZ = worldZ % Chunk.Size;
|
|
||||||
|
|
||||||
chunk.SetBlock(localX, worldY, localZ, block);
|
chunk.SetBlock(localX, worldY, localZ, block);
|
||||||
|
|
||||||
if (block == Blocks.Air && ((localX == Chunk.Size - 1 || localX == 0) || (localZ == Chunk.Size - 1 || localZ == 0)))
|
if (block == Blocks.Air && ((localX == Chunk.Size - 1 || localX == 0) || (localZ == Chunk.Size - 1 || localZ == 0)))
|
||||||
@@ -134,6 +177,30 @@ namespace Voxel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static (int chunkX, int chunkZ, int localX, int localZ) WorldToChunkCoords(int worldX, int worldZ)
|
||||||
|
{
|
||||||
|
int chunkX = worldX >= 0 ? worldX / Chunk.Size : (worldX + 1) / Chunk.Size - 1;
|
||||||
|
int chunkZ = worldZ >= 0 ? worldZ / Chunk.Size : (worldZ + 1) / Chunk.Size - 1;
|
||||||
|
|
||||||
|
int localX = ((worldX % Chunk.Size) + Chunk.Size) % Chunk.Size;
|
||||||
|
int localZ = ((worldZ % Chunk.Size) + Chunk.Size) % Chunk.Size;
|
||||||
|
|
||||||
|
return (chunkX, chunkZ, localX, localZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
List<Orientation> GetEdgeOrientations(int localX, int localZ, int size)
|
||||||
|
{
|
||||||
|
var orientations = new List<Orientation>();
|
||||||
|
|
||||||
|
if (localX == size - 1) orientations.Add(Orientation.West);
|
||||||
|
if (localX == 0) orientations.Add(Orientation.East);
|
||||||
|
if (localZ == size - 1) orientations.Add(Orientation.North);
|
||||||
|
if (localZ == 0) orientations.Add(Orientation.South);
|
||||||
|
|
||||||
|
return orientations;
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerable<(Orientation orientation, Chunk neighbor)> GetChunkNeighbors(Chunk chunk)
|
public IEnumerable<(Orientation orientation, Chunk neighbor)> GetChunkNeighbors(Chunk chunk)
|
||||||
{
|
{
|
||||||
int chunkX = chunk.X;
|
int chunkX = chunk.X;
|
||||||
|
|||||||
Reference in New Issue
Block a user