Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions assets/shaders/vulkan/shadow.vert
Original file line number Diff line number Diff line change
@@ -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;

Expand Down
Binary file modified assets/shaders/vulkan/shadow.vert.spv
Binary file not shown.
64 changes: 48 additions & 16 deletions assets/shaders/vulkan/terrain.vert
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}
}
Binary file modified assets/shaders/vulkan/terrain.vert.spv
Binary file not shown.
102 changes: 95 additions & 7 deletions src/engine/graphics/rhi_types.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
16 changes: 7 additions & 9 deletions src/engine/graphics/vulkan/pipeline_specialized.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/engine/graphics/vulkan/rhi_resource_setup.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
22 changes: 11 additions & 11 deletions src/engine/graphics/wireframe_cube.zig
Original file line number Diff line number Diff line change
@@ -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).
Expand Down
11 changes: 1 addition & 10 deletions src/engine/ui/debug_frustum.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
11 changes: 1 addition & 10 deletions src/game/block_outline.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 4 additions & 4 deletions src/game/hand_renderer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading
Loading