const std = @import("std"); const board = @import("chess/board.zig"); const piece = @import("chess/piece.zig"); pub const Vertex = extern struct { position: [2]f32, color: [4]f32, uv: [2]f32, }; pub const BoardRect = struct { left: f32, bottom: f32, width: f32, height: f32, }; pub const Light = [4]f32{ 0.85, 0.75, 0.60, 1.0 }; pub const Dark = [4]f32{ 0.35, 0.20, 0.10, 1.0 }; pub const White = [4]f32{ 1.0, 1.0, 1.0, 1.0 }; pub fn boardRectForExtent(width: u32, height: u32) BoardRect { return boardRectForExtentWithPalette(width, height, true); } pub fn boardRectForExtentWithPalette(width: u32, height: u32, show_palette: bool) BoardRect { const window_w: f32 = @floatFromInt(width); const window_h: f32 = @floatFromInt(height); const margin_px: f32 = 32.0; const palette_gap_px: f32 = if (show_palette) 16.0 else 0.0; const palette_width_ratio: f32 = if (show_palette) 1.0 / 12.0 else 0.0; const menu_area_px: f32 = 80.0; const fen_gap_ratio: f32 = 1.0 / 18.0; const fen_glyph_height_ratio: f32 = 7.0 / 95.0; const fen_area_ratio = fen_gap_ratio + fen_glyph_height_ratio; const available_w = @max(1.0, window_w - (2.0 * margin_px) - palette_gap_px); const available_h = @max(1.0, window_h - (2.0 * margin_px) - menu_area_px); const board_from_width = available_w / (1.0 + palette_width_ratio); const board_from_height = available_h / (1.0 + fen_area_ratio); const board_size_px = @min(board_from_width, board_from_height); const palette_width_px = board_size_px * palette_width_ratio; const group_width_px = board_size_px + palette_gap_px + palette_width_px; const board_left_px = (window_w - group_width_px) / 2.0; const board_bottom_px = margin_px + (board_size_px * fen_area_ratio); const max_board_top_px = window_h - margin_px - menu_area_px; const board_top_px = @min(board_bottom_px + board_size_px, max_board_top_px); const adjusted_board_size_px = @max(1.0, board_top_px - board_bottom_px); return .{ .left = (board_left_px / window_w) * 2.0 - 1.0, .bottom = (board_bottom_px / window_h) * 2.0 - 1.0, .width = (adjusted_board_size_px / window_w) * 2.0, .height = (adjusted_board_size_px / window_h) * 2.0, }; } pub fn appendQuad( vertices: *std.ArrayList(Vertex), allocator: std.mem.Allocator, bottom_left: [2]f32, bottom_right: [2]f32, top_right: [2]f32, top_left: [2]f32, color: [4]f32, ) !void { try appendTexturedQuad( vertices, allocator, bottom_left, bottom_right, top_right, top_left, color, .{ 0.0, 0.0 }, .{ 0.0, 0.0 }, .{ 0.0, 0.0 }, .{ 0.0, 0.0 }, ); } pub fn appendTexturedQuad( vertices: *std.ArrayList(Vertex), allocator: std.mem.Allocator, bottom_left: [2]f32, bottom_right: [2]f32, top_right: [2]f32, top_left: [2]f32, color: [4]f32, bottom_left_uv: [2]f32, bottom_right_uv: [2]f32, top_right_uv: [2]f32, top_left_uv: [2]f32, ) !void { try vertices.append(allocator, .{ .position = bottom_left, .color = color, .uv = bottom_left_uv, }); try vertices.append(allocator, .{ .position = bottom_right, .color = color, .uv = bottom_right_uv, }); try vertices.append(allocator, .{ .position = top_right, .color = color, .uv = top_right_uv, }); try vertices.append(allocator, .{ .position = bottom_left, .color = color, .uv = bottom_left_uv, }); try vertices.append(allocator, .{ .position = top_right, .color = color, .uv = top_right_uv, }); try vertices.append(allocator, .{ .position = top_left, .color = color, .uv = top_left_uv, }); } pub fn appendChessboard( vertices: *std.ArrayList(Vertex), board_rect: BoardRect, allocator: std.mem.Allocator, ) !void { var rank: usize = 0; while (rank < 8) : (rank += 1) { var file: usize = 0; while (file < 8) : (file += 1) { const color = if ((rank + file) % 2 == 0) Dark else Light; const x0: f32 = @floatFromInt(file); const x1: f32 = @floatFromInt(file + 1); const y0: f32 = @floatFromInt(rank); const y1: f32 = @floatFromInt(rank + 1); try appendQuad( vertices, allocator, boardToNdc(board_rect, x0, y0), boardToNdc(board_rect, x1, y0), boardToNdc(board_rect, x1, y1), boardToNdc(board_rect, x0, y1), color, ); } } } pub fn appendPieceQuad( vertices: *std.ArrayList(Vertex), board_rect: BoardRect, allocator: std.mem.Allocator, file: f32, rank: f32, ) !void { try appendPieceQuadWithColor(vertices, board_rect, allocator, file, rank, White); } pub fn appendPieceQuadWithColor( vertices: *std.ArrayList(Vertex), board_rect: BoardRect, allocator: std.mem.Allocator, file: f32, rank: f32, color: [4]f32, ) !void { const inset: f32 = 0.08; const x0 = file + inset; const x1 = file + 1.0 - inset; const y0 = rank + inset; const y1 = rank + 1.0 - inset; try appendTexturedQuad( vertices, allocator, boardToNdc(board_rect, x0, y0), boardToNdc(board_rect, x1, y0), boardToNdc(board_rect, x1, y1), boardToNdc(board_rect, x0, y1), color, .{ 0.0, 1.0 }, .{ 1.0, 1.0 }, .{ 1.0, 0.0 }, .{ 0.0, 0.0 }, ); } pub fn appendPieceQuadCenteredAtNdc( vertices: *std.ArrayList(Vertex), allocator: std.mem.Allocator, center: [2]f32, size: [2]f32, color: [4]f32, ) !void { const half_w = size[0] / 2.0; const half_h = size[1] / 2.0; const x0 = center[0] - half_w; const x1 = center[0] + half_w; const y0 = center[1] - half_h; const y1 = center[1] + half_h; try appendTexturedQuad( vertices, allocator, .{ x0, y0 }, .{ x1, y0 }, .{ x1, y1 }, .{ x0, y1 }, color, .{ 0.0, 1.0 }, .{ 1.0, 1.0 }, .{ 1.0, 0.0 }, .{ 0.0, 0.0 }, ); } pub fn appendPiecesFromBoard( vertices: *std.ArrayList(Vertex), board_rect: BoardRect, allocator: std.mem.Allocator, state: board.BoardState, ) !void { for (0..8) |rank| { for (0..8) |file| { const p = state.getSquare(@intCast((rank * 8) + file)); if (piece.typeOf(p) != piece.PieceType.none) { try appendPieceQuad(vertices, board_rect, allocator, @floatFromInt(file), @floatFromInt(rank)); } } } } pub fn boardToNdc(rect: BoardRect, x: f32, y: f32) [2]f32 { return .{ rect.left + (x / 8.0) * rect.width, rect.bottom + (y / 8.0) * rect.height, }; }