From f532eddfbb41737f88679f28a4e43af0de8146dc Mon Sep 17 00:00:00 2001 From: maxwes08 Date: Mon, 15 Dec 2025 10:14:16 +0100 Subject: [PATCH] optimized faces --- Chunk.cs | 20 ++++++++++-------- ChunkMesh.cs | 39 ++++++++++++++++++---------------- FaceData.cs | 45 ++++++++++++++++++++++++++------------- Player.cs | 7 ------- Program.cs | 28 ------------------------- Renderer.cs | 27 +++--------------------- Shaders/shader.vert | 51 ++++++++++++++++++++++++++------------------- Textures.cs | 2 +- UIElement.cs | 7 +++++++ UISprite.cs | 17 --------------- World.cs | 5 ++++- 11 files changed, 107 insertions(+), 141 deletions(-) create mode 100644 UIElement.cs delete mode 100644 UISprite.cs diff --git a/Chunk.cs b/Chunk.cs index c4df8b9..5be78f4 100644 --- a/Chunk.cs +++ b/Chunk.cs @@ -40,7 +40,7 @@ namespace Voxel Y = y; _blockData = new Dictionary(); - _blocks = new Blocks[Size * Size * Height]; + _blocks = new Blocks[TotalBlocks]; _chunkMesh = new ChunkMesh(X, Y); Initialize(); @@ -219,14 +219,16 @@ namespace Voxel private void AddFace(int x, int y, int z, int face, Textures texture, List faces) { - faces.Add(new FaceData - { - Facing = (Orientation)face, - Texture = texture, - X = (byte)x, - Y = (byte)y, - Z = (byte)z - }); + byte lightLevel = 15; + + faces.Add(new FaceData( + (byte)x, + (byte)y, + (byte)z, + (Orientation)face, + texture, + lightLevel + )); } public ChunkMesh GetChunkMesh() diff --git a/ChunkMesh.cs b/ChunkMesh.cs index f92e35f..9606a65 100644 --- a/ChunkMesh.cs +++ b/ChunkMesh.cs @@ -1,25 +1,15 @@ -using OpenTK.Graphics.OpenGL4; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Voxel +namespace Voxel { public class ChunkMesh { public int X; public int Y; private byte[] _packedData; - public bool NeedsUpdate = false; public int Size = 0; - private List _faces; public ChunkMesh(int x, int y) { - _packedData = new byte[0]; + _packedData = Array.Empty(); X = x; Y = y; } @@ -27,18 +17,31 @@ namespace Voxel public void SetFaces(List faces) { Size = faces.Count; - _faces = faces; - NeedsUpdate = true; + _packedData = PackFaces(faces); } - public byte[] GetPackedData() + private static byte[] PackFaces(List faces) { - if (NeedsUpdate) + const int BYTES_PER_FACE = 4; + int totalFaces = faces.Count; + var result = new byte[faces.Count * BYTES_PER_FACE]; + + for (int i = 0; i < totalFaces; i++) { - _packedData = _faces.SelectMany(f => f.Pack()).ToArray(); + var face = faces[i]; + uint packed = face._data; + int offset = i * 4; + + // Write little-endian (important!) + result[offset] = (byte)(packed); + result[offset + 1] = (byte)(packed >> 8); + result[offset + 2] = (byte)(packed >> 16); + result[offset + 3] = (byte)(packed >> 24); } - return _packedData; + return result; } + + public byte[] GetPackedData() => _packedData; } } diff --git a/FaceData.cs b/FaceData.cs index 81ba00d..379acf0 100644 --- a/FaceData.cs +++ b/FaceData.cs @@ -2,25 +2,42 @@ namespace Voxel { + [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct FaceData { - public Orientation Facing; - public byte X, Y, Z; - public Textures Texture; - public byte LightLevel; + public uint _data; + + // Bit layout: + // [31-24]: Y (8 bits) 0-255 + // [23-20]: Z (4 bits) 0-15 + // [19-16]: X (4 bits) 0-15 + // [15-10]: Texture (6 bits) 0-63 + // [9-7]: Facing (3 bits) 0-7 + // [6-3]: Light (4 bits) 0-15 + // [2-0]: unused (3 bits) + + public FaceData(byte x, byte y, byte z, Orientation facing, Textures texture, byte lightLevel) + { + _data = (uint)( + ((y & 0xFF) << 24) | // 8 bits + ((z & 0x0F) << 20) | // 4 bits + ((x & 0x0F) << 16) | // 4 bits + (((byte)texture & 0x3F) << 10) | // 6 bits (0-63) + (((byte)facing & 0x07) << 7) | // 3 bits + ((lightLevel & 0x0F) << 3) // 4 bits + ); + } + + public byte X => (byte)((_data >> 16) & 0x0F); + public byte Y => (byte)((_data >> 24) & 0xFF); + public byte Z => (byte)((_data >> 20) & 0x0F); + public Orientation Facing => (Orientation)((_data >> 7) & 0x07); + public Textures Texture => (Textures)((_data >> 10) & 0x3F); + public byte LightLevel => (byte)((_data >> 3) & 0x0F); public byte[] Pack() { - return new byte[] - { - X, - Y, - Z, - (byte)Facing, - (byte)Texture, - LightLevel, - 0,0 // two bits empty - }; + return BitConverter.GetBytes(_data); } } } diff --git a/Player.cs b/Player.cs index f71bbfc..b9e9cde 100644 --- a/Player.cs +++ b/Player.cs @@ -1,12 +1,6 @@ using OpenTK.Mathematics; using OpenTK.Windowing.Common; using OpenTK.Windowing.GraphicsLibraryFramework; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; namespace Voxel { @@ -66,7 +60,6 @@ namespace Voxel if (Input.GetKey(Keys.Space) && OnGround) { - Console.WriteLine("Jump"); Velocity = new Vector3(Velocity.X, 0.42f, Velocity.Z); OnGround = false; } diff --git a/Program.cs b/Program.cs index 0cf28ff..92c218d 100644 --- a/Program.cs +++ b/Program.cs @@ -12,34 +12,6 @@ internal class Program World world = new World(); Window window = new Window(sizeX, sizeY, title); - Console.WriteLine("Generating map..."); - - int worldSizeX = 2; - int worldSizeY = 2; - - float maxI = worldSizeX * worldSizeY; - int i = 0; - int lastPercentage = 0; - - for (int x = 0; x < worldSizeX; x++) - { - for (int y = 0; y < worldSizeY; y++) - { - i++; - Chunk chunk = new Chunk(x, y); - world.AddChunk(chunk); - - int percentage = (int)((i / maxI) * 100); - if (percentage > lastPercentage) - { - lastPercentage = percentage; - Console.WriteLine((percentage).ToString() + "%"); - } - } - } - - Console.WriteLine("Generated " + maxI.ToString() + " chunks"); - Renderer.SetWorld(world); Vector3 startPos = new Vector3(15, 64, 15); diff --git a/Renderer.cs b/Renderer.cs index dd42e64..0e68fb0 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -72,8 +72,8 @@ namespace Voxel byte[] data = chunkMesh.GetPackedData(); GL.BufferSubData(BufferTarget.ShaderStorageBuffer, - (IntPtr)(faceOffset * 8), // faceOffset * 8 = byte offset - chunkMesh.Size * 8, + (IntPtr)(faceOffset * 4), // faceOffset * 4 = byte offset + chunkMesh.Size * 4, data ); @@ -83,28 +83,7 @@ namespace Voxel private static void RenderUi() { - GL.Disable(EnableCap.DepthTest); - GL.Enable(EnableCap.Blend); - GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); - - //_uiShader.Use(); - - //Matrix4 projection = Matrix4.CreateOrthographicOffCenter( - // 0, screenWidth, screenHeight, 0, -1, 1); - //_uiShader.SetMatrix4("projection", projection); - - // Bind UI texture atlas - //_uiTexture.Bind(); - - // Draw all UI sprites (batch by texture for efficiency) - //foreach (var sprite in _uiSprites) - //{ - // sprite.Draw(); - //} - - // Restore 3D settings - GL.Disable(EnableCap.Blend); - GL.Enable(EnableCap.DepthTest); + } private static void RenderWorld() diff --git a/Shaders/shader.vert b/Shaders/shader.vert index 8d1aa25..b9b76e2 100644 --- a/Shaders/shader.vert +++ b/Shaders/shader.vert @@ -1,17 +1,12 @@ #version 430 core - struct FaceData { - uint x; - uint y; - uint z; - uint facing; // 0=+X,1=-X,2=+Y,3=-Y,4=+Z,5=-Z - uint texture; - uint lightLevel; - }; +struct FaceData { + uint pack; // All data in 4 bytes +}; - layout(std430, binding = 0) buffer FaceBuffer { - uint faces[]; - }; +layout(std430, binding = 0) buffer FaceBuffer { + uint faces[]; +}; uniform mat4 view; uniform mat4 projection; @@ -77,23 +72,35 @@ const vec3 offsets[6][6] = vec3[6][6]( const vec2 uvs[6] = vec2[6](vec2(0,0), vec2(1,1), vec2(1,0), vec2(1,1), vec2(0,0), vec2(0,1)); +// Function to unpack the 4-byte face data +// Bit layout from C# (little-endian, but we read as uint): +// Bits 31-24: Y (8 bits) 0-255 +// Bits 23-20: Z (4 bits) 0-15 +// Bits 19-16: X (4 bits) 0-15 +// Bits 15-10: Texture (6 bits) 0-63 +// Bits 9-7: Facing (3 bits) 0-7 +// Bits 6-3: Light (4 bits) 0-15 +// Bits 2-0: unused (3 bits) +void unpackFace(uint pack, out uint x, out uint y, out uint z, + out uint facing, out uint texture, out uint lightLevel) +{ + y = (pack >> 24) & 0xFFu; // Y: bits 24-31 (8 bits) + z = (pack >> 20) & 0x0Fu; // Z: bits 20-23 (4 bits) + x = (pack >> 16) & 0x0Fu; // X: bits 16-19 (4 bits) + texture = (pack >> 10) & 0x3Fu; // Texture: bits 10-15 (6 bits) + facing = (pack >> 7) & 0x07u; // Facing: bits 7-9 (3 bits) + lightLevel = (pack >> 3) & 0x0Fu;// Light: bits 3-6 (4 bits) +} + void main() { uint faceIndex = gl_VertexID / 6u; uint vertIndex = gl_VertexID % 6u; - uint start = faceIndex * 2u; // 2 uint per face - - uint u0 = faces[start]; // data in uint 0 - uint u1 = faces[start + 1]; // data in uint 1 + uint pack = faces[faceIndex]; - // extract values from bits - uint x = u0 & 0xFFu; - uint y = (u0 >> 8) & 0xFFu; - uint z = (u0 >> 16) & 0xFFu; - uint facing = (u0 >> 24) & 0xFFu; - uint texture = u1 & 0xFFu; - uint lightLevel = (u1 >> 8) & 0xFFu; + uint x, y, z, facing, texture, lightLevel; + unpackFace(pack, x, y, z, facing, texture, lightLevel); vec3 basePos = vec3(x, y, z) + vec3(chunkX, 0, chunkY) * 16.0; diff --git a/Textures.cs b/Textures.cs index 304df45..cbfc392 100644 --- a/Textures.cs +++ b/Textures.cs @@ -1,6 +1,6 @@ namespace Voxel { - public enum Textures : uint + public enum Textures : byte { GrassTop, Stone, diff --git a/UIElement.cs b/UIElement.cs new file mode 100644 index 0000000..8b1fa2d --- /dev/null +++ b/UIElement.cs @@ -0,0 +1,7 @@ +using OpenTK.Mathematics; +using System.Drawing; + +public class UIElement +{ + +} \ No newline at end of file diff --git a/UISprite.cs b/UISprite.cs deleted file mode 100644 index 7096e73..0000000 --- a/UISprite.cs +++ /dev/null @@ -1,17 +0,0 @@ -using OpenTK.Mathematics; -using System.Drawing; - -public class UISprite -{ - public Vector2 Position; // Screen pixels (not normalized) - public Vector2 Size; // Size in pixels - public Rectangle TextureRegion; // Which part of atlas to use - public Color Tint = Color.White; - public float Rotation = 0f; - public Vector2 Origin = Vector2.Zero; // Rotation/scale origin - - public void Draw() - { - // Draw textured quad using TextureRegion coordinates - } -} \ No newline at end of file diff --git a/World.cs b/World.cs index 7db3a9a..b965751 100644 --- a/World.cs +++ b/World.cs @@ -10,6 +10,7 @@ namespace Voxel private (int x, int z) _lastCenter = (0, 0); private int _loadDistance = 4; + bool chunkLoadingInitialized = false; private static readonly Dictionary _neighborOffsets = new() { @@ -62,7 +63,7 @@ namespace Voxel int centerZ = (int)Math.Floor(playerPosition.Z / Chunk.Size); // Quick check - skip if still in same chunk - if (centerX == _lastCenter.x && centerZ == _lastCenter.z) + if ((centerX == _lastCenter.x && centerZ == _lastCenter.z) && chunkLoadingInitialized) return; _lastCenter = (centerX, centerZ); @@ -78,6 +79,8 @@ namespace Voxel // Load chunks inside range LoadChunksInRange(minX, maxX, minZ, maxZ); + + chunkLoadingInitialized = true; } private void UnloadDistantChunks(int minX, int maxX, int minZ, int maxZ)