diff --git a/assets/shaders/vulkan/shadow.vert b/assets/shaders/vulkan/shadow.vert index 0d623091..62d14a9d 100644 --- a/assets/shaders/vulkan/shadow.vert +++ b/assets/shaders/vulkan/shadow.vert @@ -1,15 +1,28 @@ #version 450 layout(location = 0) in vec3 aPos; -layout(location = 1) in vec3 aNormal; +layout(location = 1) in uint aNormal; layout(push_constant) uniform ShadowModelUniforms { mat4 mvp; - vec4 bias_params; // x=normalBias, y=slopeBias, z=cascadeIndex, w=texelSize + vec4 bias_params; } pc; +vec3 decodeNormal(uint packed) { + vec2 oct = unpackSnorm2x16(packed); + float px = oct.x; + float py = oct.y; + float pz = 1.0 - abs(px) - abs(py); + if (pz < 0.0) { + float orig_px = px; + px = (1.0 - abs(py)) * (px >= 0.0 ? 1.0 : -1.0); + py = (1.0 - abs(orig_px)) * (py >= 0.0 ? 1.0 : -1.0); + } + return normalize(vec3(px, py, pz)); +} + void main() { - vec3 worldNormal = aNormal; + vec3 worldNormal = decodeNormal(aNormal); float normalBias = pc.bias_params.x * pc.bias_params.w; vec3 biasedPos = aPos + worldNormal * normalBias; diff --git a/assets/shaders/vulkan/shadow.vert.spv b/assets/shaders/vulkan/shadow.vert.spv index fac709d3..1996ed02 100644 Binary files a/assets/shaders/vulkan/shadow.vert.spv and b/assets/shaders/vulkan/shadow.vert.spv differ diff --git a/assets/shaders/vulkan/terrain.vert b/assets/shaders/vulkan/terrain.vert index fb74094d..9e965e3f 100644 --- a/assets/shaders/vulkan/terrain.vert +++ b/assets/shaders/vulkan/terrain.vert @@ -1,13 +1,11 @@ #version 450 layout(location = 0) in vec3 aPos; -layout(location = 1) in vec3 aColor; -layout(location = 2) in vec3 aNormal; +layout(location = 1) in uint aColor; +layout(location = 2) in uint aNormal; layout(location = 3) in vec2 aTexCoord; -layout(location = 4) in float aTileID; -layout(location = 5) in float aSkyLight; -layout(location = 6) in vec3 aBlockLight; -layout(location = 7) in float aAO; +layout(location = 4) in uint aPackedMeta; +layout(location = 5) in uint aBlockLight; layout(location = 0) out vec3 vColor; layout(location = 1) flat out vec3 vNormal; @@ -62,6 +60,27 @@ layout(push_constant) uniform ModelUniforms { float mask_radius; } model_data; +vec3 decodeColor(uint c) { + return vec3( + float(c & 0xFFu) / 255.0, + float((c >> 8u) & 0xFFu) / 255.0, + float((c >> 16u) & 0xFFu) / 255.0 + ); +} + +vec3 decodeNormal(uint packed) { + vec2 oct = unpackSnorm2x16(packed); + float px = oct.x; + float py = oct.y; + float pz = 1.0 - abs(px) - abs(py); + if (pz < 0.0) { + float orig_px = px; + px = (1.0 - abs(py)) * (px >= 0.0 ? 1.0 : -1.0); + py = (1.0 - abs(orig_px)) * (py >= 0.0 ? 1.0 : -1.0); + } + return normalize(vec3(px, py, pz)); +} + void main() { mat4 model; float mask_radius; @@ -88,28 +107,41 @@ void main() { vClipPosCurrent = vec4(clipPos.x, -clipPos.y, clipPos.z, clipPos.w); vClipPosPrev = vec4(clipPosPrev.x, -clipPosPrev.y, clipPosPrev.z, clipPosPrev.w); - vColor = aColor * color_override; - vNormal = aNormal; + vec3 decodedColor = decodeColor(aColor); + vec3 decodedNormal = decodeNormal(aNormal); + + uint tile_id_u16 = aPackedMeta & 0xFFFFu; + float skylight = float((aPackedMeta >> 16u) & 0xFFu) / 255.0; + float ao = float((aPackedMeta >> 24u) & 0xFFu) / 255.0; + + vec3 blocklight = vec3( + float(aBlockLight & 0xFFu) / 255.0, + float((aBlockLight >> 8u) & 0xFFu) / 255.0, + float((aBlockLight >> 16u) & 0xFFu) / 255.0 + ); + + vColor = decodedColor * color_override; + vNormal = decodedNormal; vTexCoord = aTexCoord; - vTileID = int(aTileID); + vTileID = (tile_id_u16 == 0xFFFFu) ? -1 : int(tile_id_u16); vDistance = length(worldPos.xyz); - vSkyLight = aSkyLight; - vBlockLight = aBlockLight; + vSkyLight = skylight; + vBlockLight = blocklight; vFragPosWorld = worldPos.xyz; vViewDepth = -clipPos.w; - vAO = aAO; + vAO = ao; vMaskRadius = mask_radius; - vec3 absNormal = abs(aNormal); + vec3 absNormal = abs(decodedNormal); if (absNormal.y > 0.9) { vTangent = vec3(1.0, 0.0, 0.0); - vBitangent = vec3(0.0, 0.0, aNormal.y > 0.0 ? 1.0 : -1.0); + vBitangent = vec3(0.0, 0.0, decodedNormal.y > 0.0 ? 1.0 : -1.0); } else if (absNormal.x > 0.9) { - vTangent = vec3(0.0, 0.0, aNormal.x > 0.0 ? -1.0 : 1.0); + vTangent = vec3(0.0, 0.0, decodedNormal.x > 0.0 ? -1.0 : 1.0); vBitangent = vec3(0.0, 1.0, 0.0); } else { - vTangent = vec3(aNormal.z > 0.0 ? 1.0 : -1.0, 0.0, 0.0); + vTangent = vec3(decodedNormal.z > 0.0 ? 1.0 : -1.0, 0.0, 0.0); vBitangent = vec3(0.0, 1.0, 0.0); } } diff --git a/assets/shaders/vulkan/terrain.vert.spv b/assets/shaders/vulkan/terrain.vert.spv index 88d105a8..2cda85eb 100644 Binary files a/assets/shaders/vulkan/terrain.vert.spv and b/assets/shaders/vulkan/terrain.vert.spv differ diff --git a/src/engine/graphics/rhi_types.zig b/src/engine/graphics/rhi_types.zig index be28426a..d2e989d4 100644 --- a/src/engine/graphics/rhi_types.zig +++ b/src/engine/graphics/rhi_types.zig @@ -92,15 +92,103 @@ pub const TextureAtlasHandles = struct { pub const Vertex = extern struct { pos: [3]f32, - color: [3]f32, - normal: [3]f32, - uv: [2]f32, - tile_id: f32, - skylight: f32, - blocklight: [3]f32, - ao: f32, + color: u32, + normal: u32, + uv: [2]f16, + packed_meta: u32, + blocklight: u32, + + pub const LOD_TILE_ID: u16 = 0xFFFF; + + pub fn init( + pos: [3]f32, + color: [3]f32, + normal: [3]f32, + uv: [2]f32, + tile_id: u16, + skylight: f32, + blocklight: [3]f32, + ao: f32, + ) Vertex { + return .{ + .pos = pos, + .color = encodeColor(color), + .normal = encodeNormal(normal), + .uv = .{ @floatCast(uv[0]), @floatCast(uv[1]) }, + .packed_meta = encodeMeta(tile_id, skylight, ao), + .blocklight = encodeBlocklight(blocklight), + }; + } + + pub fn initLOD( + pos: [3]f32, + color: [3]f32, + normal: [3]f32, + uv: [2]f32, + ) Vertex { + return .{ + .pos = pos, + .color = encodeColor(color), + .normal = encodeNormal(normal), + .uv = .{ @floatCast(uv[0]), @floatCast(uv[1]) }, + .packed_meta = encodeMeta(LOD_TILE_ID, 1.0, 1.0), + .blocklight = 0, + }; + } }; +/// Encode RGB float color to RGBA8. Alpha is always 255. +/// Precision: 1/255 per channel (~0.4% steps). Banding may be visible on smooth gradients. +pub fn encodeColor(c: [3]f32) u32 { + const r: u8 = @intFromFloat(@round(@max(0.0, @min(1.0, c[0])) * 255.0)); + const g: u8 = @intFromFloat(@round(@max(0.0, @min(1.0, c[1])) * 255.0)); + const b: u8 = @intFromFloat(@round(@max(0.0, @min(1.0, c[2])) * 255.0)); + return @as(u32, r) | (@as(u32, g) << 8) | (@as(u32, b) << 16) | (@as(u32, @as(u8, 255)) << 24); +} + +/// Encode a unit normal via octahedral mapping to 2×i16 packed in a u32. +/// Precision: ~0.1° angular error for axis-aligned normals, up to ~0.3° for diagonals. +/// Zero-length normals encode as 0 (decode as up). +pub fn encodeNormal(n: [3]f32) u32 { + const l1 = @abs(n[0]) + @abs(n[1]) + @abs(n[2]); + if (l1 < 0.0001) return 0; + + var px = n[0] / l1; + var py = n[1] / l1; + + if (n[2] < 0.0) { + const orig_px = px; + const sign_x: f32 = if (px >= 0.0) 1.0 else -1.0; + const sign_y: f32 = if (py >= 0.0) 1.0 else -1.0; + px = (1.0 - @abs(py)) * sign_x; + py = (1.0 - @abs(orig_px)) * sign_y; + } + + const sx: i16 = @intFromFloat(@round(@max(-1.0, @min(1.0, px)) * 32767.0)); + const sy: i16 = @intFromFloat(@round(@max(-1.0, @min(1.0, py)) * 32767.0)); + const ux: u16 = @bitCast(sx); + const uy: u16 = @bitCast(sy); + return @as(u32, ux) | (@as(u32, uy) << 16); +} + +/// Pack tile_id (u16), skylight (u8), and AO (u8) into a single u32. +/// Skylight and AO precision: 1/255 (~0.4% steps). Tile IDs 0-65534 valid; 0xFFFF is LOD sentinel. +pub fn encodeMeta(tile_id: u16, skylight: f32, ao: f32) u32 { + std.debug.assert(tile_id != Vertex.LOD_TILE_ID); + const sl: u8 = @intFromFloat(@round(@max(0.0, @min(1.0, skylight)) * 255.0)); + const ao_u8: u8 = @intFromFloat(@round(@max(0.0, @min(1.0, ao)) * 255.0)); + return @as(u32, tile_id) | (@as(u32, sl) << 16) | (@as(u32, ao_u8) << 24); +} + +/// Encode RGB blocklight float values to RGB8 u32 (upper 8 bits unused). +/// Precision: 1/255 per channel. Sufficient for per-vertex lighting where values blend smoothly. +pub fn encodeBlocklight(bl: [3]f32) u32 { + const r: u8 = @intFromFloat(@round(@max(0.0, @min(1.0, bl[0])) * 255.0)); + const g: u8 = @intFromFloat(@round(@max(0.0, @min(1.0, bl[1])) * 255.0)); + const b: u8 = @intFromFloat(@round(@max(0.0, @min(1.0, bl[2])) * 255.0)); + return @as(u32, r) | (@as(u32, g) << 8) | (@as(u32, b) << 16); +} + pub const DrawMode = enum { triangles, lines, diff --git a/src/engine/graphics/vulkan/pipeline_specialized.zig b/src/engine/graphics/vulkan/pipeline_specialized.zig index 002118af..05c12d5e 100644 --- a/src/engine/graphics/vulkan/pipeline_specialized.zig +++ b/src/engine/graphics/vulkan/pipeline_specialized.zig @@ -54,21 +54,19 @@ pub fn createTerrainPipeline( const binding_description = c.VkVertexInputBindingDescription{ .binding = 0, .stride = @sizeOf(rhi.Vertex), .inputRate = c.VK_VERTEX_INPUT_RATE_VERTEX }; - var attribute_descriptions: [8]c.VkVertexInputAttributeDescription = undefined; + var attribute_descriptions: [6]c.VkVertexInputAttributeDescription = undefined; attribute_descriptions[0] = .{ .binding = 0, .location = 0, .format = c.VK_FORMAT_R32G32B32_SFLOAT, .offset = 0 }; - attribute_descriptions[1] = .{ .binding = 0, .location = 1, .format = c.VK_FORMAT_R32G32B32_SFLOAT, .offset = 3 * 4 }; - attribute_descriptions[2] = .{ .binding = 0, .location = 2, .format = c.VK_FORMAT_R32G32B32_SFLOAT, .offset = 6 * 4 }; - attribute_descriptions[3] = .{ .binding = 0, .location = 3, .format = c.VK_FORMAT_R32G32_SFLOAT, .offset = 9 * 4 }; - attribute_descriptions[4] = .{ .binding = 0, .location = 4, .format = c.VK_FORMAT_R32_SFLOAT, .offset = 11 * 4 }; - attribute_descriptions[5] = .{ .binding = 0, .location = 5, .format = c.VK_FORMAT_R32_SFLOAT, .offset = 12 * 4 }; - attribute_descriptions[6] = .{ .binding = 0, .location = 6, .format = c.VK_FORMAT_R32G32B32_SFLOAT, .offset = 13 * 4 }; - attribute_descriptions[7] = .{ .binding = 0, .location = 7, .format = c.VK_FORMAT_R32_SFLOAT, .offset = 16 * 4 }; + attribute_descriptions[1] = .{ .binding = 0, .location = 1, .format = c.VK_FORMAT_R32_UINT, .offset = 12 }; + attribute_descriptions[2] = .{ .binding = 0, .location = 2, .format = c.VK_FORMAT_R32_UINT, .offset = 16 }; + attribute_descriptions[3] = .{ .binding = 0, .location = 3, .format = c.VK_FORMAT_R16G16_SFLOAT, .offset = 20 }; + attribute_descriptions[4] = .{ .binding = 0, .location = 4, .format = c.VK_FORMAT_R32_UINT, .offset = 24 }; + attribute_descriptions[5] = .{ .binding = 0, .location = 5, .format = c.VK_FORMAT_R32_UINT, .offset = 28 }; var vertex_input_info = std.mem.zeroes(c.VkPipelineVertexInputStateCreateInfo); vertex_input_info.sType = c.VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertex_input_info.vertexBindingDescriptionCount = 1; vertex_input_info.pVertexBindingDescriptions = &binding_description; - vertex_input_info.vertexAttributeDescriptionCount = 8; + vertex_input_info.vertexAttributeDescriptionCount = 6; vertex_input_info.pVertexAttributeDescriptions = &attribute_descriptions[0]; var pipeline_info = std.mem.zeroes(c.VkGraphicsPipelineCreateInfo); diff --git a/src/engine/graphics/vulkan/rhi_resource_setup.zig b/src/engine/graphics/vulkan/rhi_resource_setup.zig index a4b87904..bdaeeeae 100644 --- a/src/engine/graphics/vulkan/rhi_resource_setup.zig +++ b/src/engine/graphics/vulkan/rhi_resource_setup.zig @@ -144,7 +144,7 @@ pub fn createShadowResources(ctx: anytype) !void { const shadow_binding = c.VkVertexInputBindingDescription{ .binding = 0, .stride = @sizeOf(rhi.Vertex), .inputRate = c.VK_VERTEX_INPUT_RATE_VERTEX }; var shadow_attrs: [2]c.VkVertexInputAttributeDescription = undefined; shadow_attrs[0] = .{ .binding = 0, .location = 0, .format = c.VK_FORMAT_R32G32B32_SFLOAT, .offset = 0 }; - shadow_attrs[1] = .{ .binding = 0, .location = 1, .format = c.VK_FORMAT_R32G32B32_SFLOAT, .offset = 24 }; + shadow_attrs[1] = .{ .binding = 0, .location = 1, .format = c.VK_FORMAT_R32_UINT, .offset = 16 }; var shadow_vertex_input = std.mem.zeroes(c.VkPipelineVertexInputStateCreateInfo); shadow_vertex_input.sType = c.VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; diff --git a/src/engine/graphics/wireframe_cube.zig b/src/engine/graphics/wireframe_cube.zig index 29467788..98a5ffc2 100644 --- a/src/engine/graphics/wireframe_cube.zig +++ b/src/engine/graphics/wireframe_cube.zig @@ -1,20 +1,20 @@ //! Shared wireframe cube geometry (line list). const rhi_pkg = @import("rhi.zig"); +const rhi_types = @import("rhi_types.zig"); const Vertex = rhi_pkg.Vertex; -/// Create a vertex with the given position. fn makeVertex(x: f32, y: f32, z: f32) Vertex { - return .{ - .pos = .{ x, y, z }, - .color = .{ 1.0, 1.0, 1.0 }, - .normal = .{ 0, 1, 0 }, - .uv = .{ 0, 0 }, - .tile_id = 0, - .skylight = 15, - .blocklight = .{ 15, 15, 15 }, - .ao = 1.0, - }; + return rhi_types.Vertex.init( + .{ x, y, z }, + .{ 1.0, 1.0, 1.0 }, + .{ 0, 1, 0 }, + .{ 0, 0 }, + 0, + 1.0, + .{ 1.0, 1.0, 1.0 }, + 1.0, + ); } /// Vertices for a 1x1x1 cube wireframe (line list, 12 edges). diff --git a/src/engine/ui/debug_frustum.zig b/src/engine/ui/debug_frustum.zig index f6489103..7e7424f1 100644 --- a/src/engine/ui/debug_frustum.zig +++ b/src/engine/ui/debug_frustum.zig @@ -12,16 +12,7 @@ pub const FRUSTUM_EDGE_COUNT: u32 = 12; pub const FRUSTUM_VERTEX_COUNT: u32 = FRUSTUM_EDGE_COUNT * 2; fn makeVertex(x: f32, y: f32, z: f32, r: f32, g: f32, b: f32) Vertex { - return .{ - .pos = .{ x, y, z }, - .color = .{ r, g, b }, - .normal = .{ 0, 1, 0 }, - .uv = .{ 0, 0 }, - .tile_id = 0, - .skylight = 15, - .blocklight = .{ 15, 15, 15 }, - .ao = 1.0, - }; + return Vertex.init(.{ x, y, z }, .{ r, g, b }, .{ 0, 1, 0 }, .{ 0, 0 }, 0, 1.0, .{ 1.0, 1.0, 1.0 }, 1.0); } pub const DebugFrustum = struct { diff --git a/src/game/block_outline.zig b/src/game/block_outline.zig index c788255d..6fa9feff 100644 --- a/src/game/block_outline.zig +++ b/src/game/block_outline.zig @@ -17,16 +17,7 @@ const EXPAND: f32 = 0.004; /// Create a vertex with the given position fn makeVertex(x: f32, y: f32, z: f32) Vertex { - return .{ - .pos = .{ x, y, z }, - .color = .{ 0.0, 0.0, 0.0 }, // Black outline - .normal = .{ 0, 1, 0 }, - .uv = .{ 0, 0 }, - .tile_id = 0, - .skylight = 15, - .blocklight = .{ 15, 15, 15 }, - .ao = 1.0, - }; + return Vertex.init(.{ x, y, z }, .{ 0.0, 0.0, 0.0 }, .{ 0, 1, 0 }, .{ 0, 0 }, 0, 1.0, .{ 1.0, 1.0, 1.0 }, 1.0); } /// Generate vertices for a thin quad (2 triangles = 6 vertices) diff --git a/src/game/hand_renderer.zig b/src/game/hand_renderer.zig index 871a981d..4a33f936 100644 --- a/src/game/hand_renderer.zig +++ b/src/game/hand_renderer.zig @@ -117,10 +117,10 @@ pub const HandRenderer = struct { } fn addQuad(verts: *[36]Vertex, idx: *usize, p0: [3]f32, p1: [3]f32, p2: [3]f32, p3: [3]f32, normal: [3]f32, tile: u16, color: [3]f32) void { - const v0 = Vertex{ .pos = p0, .color = color, .normal = normal, .uv = .{ 0, 0 }, .tile_id = @floatFromInt(tile), .skylight = 1.0, .blocklight = .{ 1.0, 1.0, 1.0 }, .ao = 1.0 }; - const v1 = Vertex{ .pos = p1, .color = color, .normal = normal, .uv = .{ 1, 0 }, .tile_id = @floatFromInt(tile), .skylight = 1.0, .blocklight = .{ 1.0, 1.0, 1.0 }, .ao = 1.0 }; - const v2 = Vertex{ .pos = p2, .color = color, .normal = normal, .uv = .{ 1, 1 }, .tile_id = @floatFromInt(tile), .skylight = 1.0, .blocklight = .{ 1.0, 1.0, 1.0 }, .ao = 1.0 }; - const v3 = Vertex{ .pos = p3, .color = color, .normal = normal, .uv = .{ 0, 1 }, .tile_id = @floatFromInt(tile), .skylight = 1.0, .blocklight = .{ 1.0, 1.0, 1.0 }, .ao = 1.0 }; + const v0 = Vertex.init(p0, color, normal, .{ 0, 0 }, tile, 1.0, .{ 1.0, 1.0, 1.0 }, 1.0); + const v1 = Vertex.init(p1, color, normal, .{ 1, 0 }, tile, 1.0, .{ 1.0, 1.0, 1.0 }, 1.0); + const v2 = Vertex.init(p2, color, normal, .{ 1, 1 }, tile, 1.0, .{ 1.0, 1.0, 1.0 }, 1.0); + const v3 = Vertex.init(p3, color, normal, .{ 0, 1 }, tile, 1.0, .{ 1.0, 1.0, 1.0 }, 1.0); // Triangle 1: 0 -> 2 -> 1 (CCW) verts[idx.*] = v0; diff --git a/src/world/lod_mesh.zig b/src/world/lod_mesh.zig index 3cbd6bfc..de733d80 100644 --- a/src/world/lod_mesh.zig +++ b/src/world/lod_mesh.zig @@ -22,6 +22,10 @@ const rhi_types = @import("../engine/graphics/rhi_types.zig"); const Vertex = rhi_types.Vertex; const BufferHandle = rhi_types.BufferHandle; const RhiError = rhi_types.RhiError; +const encodeColor = rhi_types.encodeColor; +const encodeNormal = rhi_types.encodeNormal; +const encodeMeta = rhi_types.encodeMeta; +const encodeBlocklight = rhi_types.encodeBlocklight; /// Size of each LOD mesh grid cell in blocks pub fn getCellSize(lod: LODLevel) u32 { @@ -287,6 +291,17 @@ fn averageColor(c00: u32, c10: u32, c01: u32, c11: u32) u32 { return (r_avg << 16) | (g_avg << 8) | b_avg; } +fn makeLODVertex(pos: [3]f32, col: [3]f32, norm: [3]f32, uv: [2]f32) Vertex { + return Vertex{ + .pos = pos, + .color = encodeColor(col), + .normal = encodeNormal(norm), + .uv = .{ @floatCast(uv[0]), @floatCast(uv[1]) }, + .packed_meta = encodeMeta(Vertex.LOD_TILE_ID, 1.0, 1.0), + .blocklight = 0, + }; +} + /// Add a smooth quad with per-vertex heights and colors fn addSmoothQuad( allocator: std.mem.Allocator, @@ -340,68 +355,13 @@ fn addSmoothQuad( } // Triangle 1: (0,0), (1,1), (1,0) - try vertices.append(allocator, .{ - .pos = .{ x, y00, z }, - .color = .{ unpackR(c00), unpackG(c00), unpackB(c00) }, - .normal = n1, - .uv = .{ 0, 0 }, - .tile_id = -1.0, - .skylight = 1.0, - .blocklight = .{ 0, 0, 0 }, - .ao = 1.0, - }); - try vertices.append(allocator, .{ - .pos = .{ x + size, y11, z + size }, - .color = .{ unpackR(c11), unpackG(c11), unpackB(c11) }, - .normal = n1, - .uv = .{ 1, 1 }, - .tile_id = -1.0, - .skylight = 1.0, - .blocklight = .{ 0, 0, 0 }, - .ao = 1.0, - }); - try vertices.append(allocator, .{ - .pos = .{ x + size, y10, z }, - .color = .{ unpackR(c10), unpackG(c10), unpackB(c10) }, - .normal = n1, - .uv = .{ 1, 0 }, - .tile_id = -1.0, - .skylight = 1.0, - .blocklight = .{ 0, 0, 0 }, - .ao = 1.0, - }); - - // Triangle 2: (0,0), (0,1), (1,1) - try vertices.append(allocator, .{ - .pos = .{ x, y00, z }, - .color = .{ unpackR(c00), unpackG(c00), unpackB(c00) }, - .normal = n2, - .uv = .{ 0, 0 }, - .tile_id = -1.0, - .skylight = 1.0, - .blocklight = .{ 0, 0, 0 }, - .ao = 1.0, - }); - try vertices.append(allocator, .{ - .pos = .{ x, y01, z + size }, - .color = .{ unpackR(c01), unpackG(c01), unpackB(c01) }, - .normal = n2, - .uv = .{ 0, 1 }, - .tile_id = -1.0, - .skylight = 1.0, - .blocklight = .{ 0, 0, 0 }, - .ao = 1.0, - }); - try vertices.append(allocator, .{ - .pos = .{ x + size, y11, z + size }, - .color = .{ unpackR(c11), unpackG(c11), unpackB(c11) }, - .normal = n2, - .uv = .{ 1, 1 }, - .tile_id = -1.0, - .skylight = 1.0, - .blocklight = .{ 0, 0, 0 }, - .ao = 1.0, - }); + try vertices.append(allocator, makeLODVertex(.{ x, y00, z }, .{ unpackR(c00), unpackG(c00), unpackB(c00) }, n1, .{ 0, 0 })); + try vertices.append(allocator, makeLODVertex(.{ x + size, y11, z + size }, .{ unpackR(c11), unpackG(c11), unpackB(c11) }, n1, .{ 1, 1 })); + try vertices.append(allocator, makeLODVertex(.{ x + size, y10, z }, .{ unpackR(c10), unpackG(c10), unpackB(c10) }, n1, .{ 1, 0 })); + + try vertices.append(allocator, makeLODVertex(.{ x, y00, z }, .{ unpackR(c00), unpackG(c00), unpackB(c00) }, n2, .{ 0, 0 })); + try vertices.append(allocator, makeLODVertex(.{ x, y01, z + size }, .{ unpackR(c01), unpackG(c01), unpackB(c01) }, n2, .{ 0, 1 })); + try vertices.append(allocator, makeLODVertex(.{ x + size, y11, z + size }, .{ unpackR(c11), unpackG(c11), unpackB(c11) }, n2, .{ 1, 1 })); } /// Add a top-facing quad (two triangles) @@ -409,71 +369,13 @@ fn addTopFaceQuad(allocator: std.mem.Allocator, vertices: *std.ArrayListUnmanage const normal = [3]f32{ 0, 1, 0 }; const color = [3]f32{ r, g, b }; - // Triangle 1: (0,0), (1,1), (0,1) - CCW for Up Normal - try vertices.append(allocator, .{ - .pos = .{ x, y, z }, - .color = color, - .normal = normal, - .uv = .{ 0, 0 }, - .tile_id = -1.0, - .skylight = 1.0, - .blocklight = .{ 0, 0, 0 }, - .ao = 1.0, - }); - try vertices.append(allocator, .{ - .pos = .{ x + size, y, z + size }, - .color = color, - .normal = normal, - .uv = .{ 1, 1 }, - .tile_id = -1.0, - .skylight = 1.0, - .blocklight = .{ 0, 0, 0 }, - .ao = 1.0, - }); - try vertices.append(allocator, .{ - .pos = .{ x, y, z + size }, - .color = color, - .normal = normal, - .uv = .{ 0, 1 }, - .tile_id = -1.0, - .skylight = 1.0, - .blocklight = .{ 0, 0, 0 }, - .ao = 1.0, - }); - - // Triangle 2: (0,0), (1,0), (1,1) - CCW for Up Normal (Need 0->2->1 order from original points) - // Original points: 0(0,0), 1(1,0), 2(1,1). 0->1->2 is CW. 0->2->1 is CCW. - // So: (0,0), (1,1), (1,0). - try vertices.append(allocator, .{ - .pos = .{ x, y, z }, - .color = color, - .normal = normal, - .uv = .{ 0, 0 }, - .tile_id = -1.0, - .skylight = 1.0, - .blocklight = .{ 0, 0, 0 }, - .ao = 1.0, - }); - try vertices.append(allocator, .{ - .pos = .{ x + size, y, z + size }, - .color = color, - .normal = normal, - .uv = .{ 1, 1 }, - .tile_id = -1.0, - .skylight = 1.0, - .blocklight = .{ 0, 0, 0 }, - .ao = 1.0, - }); - try vertices.append(allocator, .{ - .pos = .{ x + size, y, z }, - .color = color, - .normal = normal, - .uv = .{ 1, 0 }, - .tile_id = -1.0, - .skylight = 1.0, - .blocklight = .{ 0, 0, 0 }, - .ao = 1.0, - }); + try vertices.append(allocator, makeLODVertex(.{ x, y, z }, color, normal, .{ 0, 0 })); + try vertices.append(allocator, makeLODVertex(.{ x + size, y, z + size }, color, normal, .{ 1, 1 })); + try vertices.append(allocator, makeLODVertex(.{ x, y, z + size }, color, normal, .{ 0, 1 })); + + try vertices.append(allocator, makeLODVertex(.{ x, y, z }, color, normal, .{ 0, 0 })); + try vertices.append(allocator, makeLODVertex(.{ x + size, y, z + size }, color, normal, .{ 1, 1 })); + try vertices.append(allocator, makeLODVertex(.{ x + size, y, z }, color, normal, .{ 1, 0 })); } /// Add a side-facing quad for cliff faces @@ -515,15 +417,13 @@ fn addSideFaceQuad(allocator: std.mem.Allocator, vertices: *std.ArrayListUnmanag }, }; - // Triangle 1 - try vertices.append(allocator, .{ .pos = corners[0], .color = color, .normal = normal, .uv = .{ 0, 0 }, .tile_id = -1.0, .skylight = 1.0, .blocklight = .{ 0, 0, 0 }, .ao = 1.0 }); - try vertices.append(allocator, .{ .pos = corners[1], .color = color, .normal = normal, .uv = .{ 1, 0 }, .tile_id = -1.0, .skylight = 1.0, .blocklight = .{ 0, 0, 0 }, .ao = 1.0 }); - try vertices.append(allocator, .{ .pos = corners[2], .color = color, .normal = normal, .uv = .{ 1, 1 }, .tile_id = -1.0, .skylight = 1.0, .blocklight = .{ 0, 0, 0 }, .ao = 1.0 }); + try vertices.append(allocator, makeLODVertex(corners[0], color, normal, .{ 0, 0 })); + try vertices.append(allocator, makeLODVertex(corners[1], color, normal, .{ 1, 0 })); + try vertices.append(allocator, makeLODVertex(corners[2], color, normal, .{ 1, 1 })); - // Triangle 2 - try vertices.append(allocator, .{ .pos = corners[0], .color = color, .normal = normal, .uv = .{ 0, 0 }, .tile_id = -1.0, .skylight = 1.0, .blocklight = .{ 0, 0, 0 }, .ao = 1.0 }); - try vertices.append(allocator, .{ .pos = corners[2], .color = color, .normal = normal, .uv = .{ 1, 1 }, .tile_id = -1.0, .skylight = 1.0, .blocklight = .{ 0, 0, 0 }, .ao = 1.0 }); - try vertices.append(allocator, .{ .pos = corners[3], .color = color, .normal = normal, .uv = .{ 0, 1 }, .tile_id = -1.0, .skylight = 1.0, .blocklight = .{ 0, 0, 0 }, .ao = 1.0 }); + try vertices.append(allocator, makeLODVertex(corners[0], color, normal, .{ 0, 0 })); + try vertices.append(allocator, makeLODVertex(corners[2], color, normal, .{ 1, 1 })); + try vertices.append(allocator, makeLODVertex(corners[3], color, normal, .{ 0, 1 })); } /// LOD Mesh Builder - builds meshes for LOD regions diff --git a/src/world/meshing/cross_mesher.zig b/src/world/meshing/cross_mesher.zig index 7c008fae..c7643627 100644 --- a/src/world/meshing/cross_mesher.zig +++ b/src/world/meshing/cross_mesher.zig @@ -59,14 +59,14 @@ pub fn meshCrossBlocks( }; const tiles = atlas.getTilesForBlock(@intFromEnum(block)); - const tid: f32 = @floatFromInt(tiles.side); + const tile_id: u16 = @intCast(tiles.side); const xf: f32 = @floatFromInt(x); const yf: f32 = @floatFromInt(y); const zf: f32 = @floatFromInt(z); - try emitCrossQuad(allocator, cutout_list, .{ xf, yf, zf }, .{ xf + 1, yf + 1, zf + 1 }, col, norm_light, tid); - try emitCrossQuad(allocator, cutout_list, .{ xf + 1, yf, zf }, .{ xf, yf + 1, zf + 1 }, col, norm_light, tid); + try emitCrossQuad(allocator, cutout_list, .{ xf, yf, zf }, .{ xf + 1, yf + 1, zf + 1 }, col, norm_light, tile_id); + try emitCrossQuad(allocator, cutout_list, .{ xf + 1, yf, zf }, .{ xf, yf + 1, zf + 1 }, col, norm_light, tile_id); } } } @@ -79,7 +79,7 @@ fn emitCrossQuad( p1: [3]f32, col: [3]f32, light: lighting_sampler.NormalizedLight, - tid: f32, + tile_id: u16, ) !void { const bl = [3]f32{ p0[0], p0[1], p0[2] }; const br = [3]f32{ p1[0], p0[1], p1[2] }; @@ -97,19 +97,17 @@ fn emitCrossQuad( const positions = [4][3]f32{ bl, br, tr, tl }; const uv = [4][2]f32{ .{ 0, 1 }, .{ 1, 1 }, .{ 1, 0 }, .{ 0, 0 } }; - // Front face (2 triangles) - try verts.append(allocator, Vertex{ .pos = positions[0], .color = col, .normal = nf_front, .uv = uv[0], .tile_id = tid, .skylight = light.skylight, .blocklight = light.blocklight, .ao = ao }); - try verts.append(allocator, Vertex{ .pos = positions[1], .color = col, .normal = nf_front, .uv = uv[1], .tile_id = tid, .skylight = light.skylight, .blocklight = light.blocklight, .ao = ao }); - try verts.append(allocator, Vertex{ .pos = positions[2], .color = col, .normal = nf_front, .uv = uv[2], .tile_id = tid, .skylight = light.skylight, .blocklight = light.blocklight, .ao = ao }); - try verts.append(allocator, Vertex{ .pos = positions[0], .color = col, .normal = nf_front, .uv = uv[0], .tile_id = tid, .skylight = light.skylight, .blocklight = light.blocklight, .ao = ao }); - try verts.append(allocator, Vertex{ .pos = positions[2], .color = col, .normal = nf_front, .uv = uv[2], .tile_id = tid, .skylight = light.skylight, .blocklight = light.blocklight, .ao = ao }); - try verts.append(allocator, Vertex{ .pos = positions[3], .color = col, .normal = nf_front, .uv = uv[3], .tile_id = tid, .skylight = light.skylight, .blocklight = light.blocklight, .ao = ao }); - - // Back face (2 triangles, reversed winding) - try verts.append(allocator, Vertex{ .pos = positions[1], .color = col, .normal = nf_back, .uv = uv[1], .tile_id = tid, .skylight = light.skylight, .blocklight = light.blocklight, .ao = ao }); - try verts.append(allocator, Vertex{ .pos = positions[0], .color = col, .normal = nf_back, .uv = uv[0], .tile_id = tid, .skylight = light.skylight, .blocklight = light.blocklight, .ao = ao }); - try verts.append(allocator, Vertex{ .pos = positions[3], .color = col, .normal = nf_back, .uv = uv[3], .tile_id = tid, .skylight = light.skylight, .blocklight = light.blocklight, .ao = ao }); - try verts.append(allocator, Vertex{ .pos = positions[1], .color = col, .normal = nf_back, .uv = uv[1], .tile_id = tid, .skylight = light.skylight, .blocklight = light.blocklight, .ao = ao }); - try verts.append(allocator, Vertex{ .pos = positions[3], .color = col, .normal = nf_back, .uv = uv[3], .tile_id = tid, .skylight = light.skylight, .blocklight = light.blocklight, .ao = ao }); - try verts.append(allocator, Vertex{ .pos = positions[2], .color = col, .normal = nf_back, .uv = uv[2], .tile_id = tid, .skylight = light.skylight, .blocklight = light.blocklight, .ao = ao }); + const v = [6][3]f32{ positions[0], positions[1], positions[2], positions[0], positions[2], positions[3] }; + const u = [6][2]f32{ uv[0], uv[1], uv[2], uv[0], uv[2], uv[3] }; + const n_front = [6][3]f32{ nf_front, nf_front, nf_front, nf_front, nf_front, nf_front }; + + for (0..6) |i| { + try verts.append(allocator, Vertex.init(v[i], col, n_front[i], u[i], tile_id, light.skylight, light.blocklight, ao)); + } + + const n_back = [6][3]f32{ nf_back, nf_back, nf_back, nf_back, nf_back, nf_back }; + const back_order = [6]usize{ 1, 0, 3, 1, 3, 2 }; + for (0..6) |i| { + try verts.append(allocator, Vertex.init(positions[back_order[i]], col, n_back[i], uv[back_order[i]], tile_id, light.skylight, light.blocklight, ao)); + } } diff --git a/src/world/meshing/greedy_mesher.zig b/src/world/meshing/greedy_mesher.zig index e699c9be..a68c217c 100644 --- a/src/world/meshing/greedy_mesher.zig +++ b/src/world/meshing/greedy_mesher.zig @@ -276,15 +276,15 @@ fn addGreedyFace( const norm_light = lighting_sampler.normalizeLightValues(light); for (idxs) |i| { - try verts.append(allocator, Vertex{ - .pos = p[i], - .color = col, - .normal = nf, - .uv = uv[i], - .tile_id = tid, - .skylight = norm_light.skylight, - .blocklight = norm_light.blocklight, - .ao = ao[i], - }); + try verts.append(allocator, Vertex.init( + p[i], + col, + nf, + uv[i], + @as(u16, @intFromFloat(@round(@max(0, @min(65534, tid))))), + norm_light.skylight, + norm_light.blocklight, + ao[i], + )); } }