This commit is contained in:
LordMZTE 2022-11-26 15:15:36 +01:00
commit 5a0e3e311a
Signed by: LordMZTE
GPG Key ID: B64802DC33A64FF6
19 changed files with 5959 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
zig-cache/
zig-out/
deps.zig
gyro.lock
.gyro

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "zalgebra"]
path = zalgebra
url = https://github.com/kooparse/zalgebra.git

17
README.md Normal file
View File

@ -0,0 +1,17 @@
# bingus
An OpenGL based bingo GUI, mostly meant for [porting bingo](https://git.tilera.org/tilera/portingbingo)
## dependencies
- OpenGL
- GLFW3
- freetype
- Zig
```bash
paru -S --needed \
glfw-x11 \ # or glfw-wayland
glew \
freetype2 \
zig-git # `zig` arch package is completely outdated
```

44
build.zig Normal file
View File

@ -0,0 +1,44 @@
const std = @import("std");
pub fn build(b: *std.build.Builder) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});
// Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions();
const exe = b.addExecutable("bingus", "src/main.zig");
exe.setTarget(target);
exe.setBuildMode(mode);
exe.addPackage(std.build.Pkg{
.name = "zalgebra",
.source = .{ .path = "zalgebra/src/main.zig" },
});
exe.linkLibC();
exe.linkSystemLibrary("glfw3");
exe.linkSystemLibrary("freetype2");
exe.install();
const run_cmd = exe.run();
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
const exe_tests = b.addTest("src/main.zig");
exe_tests.setTarget(target);
exe_tests.setBuildMode(mode);
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&exe_tests.step);
}

47
src/Color.zig Normal file
View File

@ -0,0 +1,47 @@
// https://draculatheme.com
pub const background = fromBytes(.{ 0x28, 0x2a, 0x36, 0xb0 });
pub const square_background = fromBytes(.{ 0x44, 0x47, 0x5a, 0xff });
pub const square_frame = fromBytes(.{ 0x50, 0xfa, 0x7b, 0xff });
r: f32 = 0.0,
g: f32 = 0.0,
b: f32 = 0.0,
a: f32 = 1.0,
const Self = @This();
pub fn fromBytes(data: [4]u8) Self {
return fromArray(
@as(@Vector(4, f32), [_]f32{
@intToFloat(f32, data[0]),
@intToFloat(f32, data[1]),
@intToFloat(f32, data[2]),
@intToFloat(f32, data[3]),
}) / @splat(4, @as(f32, 255.0)),
);
}
pub fn fromArray(data: [4]f32) Self {
return .{
.r = data[0],
.g = data[1],
.b = data[2],
.a = data[3],
};
}
pub fn toArray(self: Self) [4]f32 {
return .{
self.r,
self.g,
self.b,
self.a,
};
}
pub fn mul(self: Self, other: Self) Self {
return fromArray(
@as(@Vector(4, f32), self.toArray()) *
@as(@Vector(4, f32), other.toArray()),
);
}

30
src/GlState.zig Normal file
View File

@ -0,0 +1,30 @@
const std = @import("std");
const zalgebra = @import("zalgebra");
pub const inital_win_size = zalgebra.Vec2_i32.new(800, 800);
// I'm not a fan of global state either, but this is really not possible to do
// otherwise becuase OpenGL also has global state.
pub var state: Self = undefined;
current_screen_size: zalgebra.Vec2_i32 = inital_win_size,
vbo: c_uint = 0,
vao: c_uint = 0,
shader_program: c_uint = 0,
font_vbo: c_uint = 0,
font_vao: c_uint = 0,
font_shader_program: c_uint = 0,
const Self = @This();
pub fn init() !void {
state = .{};
}
pub fn deinit() void {
state = undefined;
}

44
src/bingoloader.zig Normal file
View File

@ -0,0 +1,44 @@
const std = @import("std");
pub const BingoData = struct {
bingos: [][]const u8,
arena: std.heap.ArenaAllocator,
pub fn deinit(self: *BingoData) void {
self.arena.deinit();
self.* = undefined;
}
};
pub fn loadFile(path: []const u8, count: usize) !BingoData {
var arena = std.heap.ArenaAllocator.init(std.heap.c_allocator);
const file = try std.fs.cwd().openFile(path, .{});
defer file.close();
const data = try file.readToEndAlloc(arena.allocator(), std.math.maxInt(usize));
var bingos = std.ArrayList([]const u8).init(std.heap.c_allocator);
defer bingos.deinit();
var data_iter = std.mem.tokenize(u8, data, ",\n");
while (data_iter.next()) |bingo| {
const trimmed = std.mem.trim(u8, bingo, "\t\n ");
if (trimmed.len == 0)
continue;
try bingos.append(trimmed);
}
var rng = std.rand.DefaultPrng.init(@bitCast(usize, std.time.microTimestamp()));
rng.random().shuffle([]const u8, bingos.items);
const bingo_items = try arena.allocator().alloc([]const u8, count);
var i: usize = 0;
while (i < count) : (i += 1) {
bingo_items[i] = if (bingos.items.len > i) bingos.items[i] else "";
}
return .{
.bingos = bingo_items,
.arena = arena,
};
}

5
src/ffi.zig Normal file
View File

@ -0,0 +1,5 @@
pub const c = @cImport({
@cInclude("freetype/freetype.h");
@cInclude("GL/glew.h");
@cInclude("GLFW/glfw3.h");
});

212
src/font.zig Normal file
View File

@ -0,0 +1,212 @@
const std = @import("std");
const zalgebra = @import("zalgebra");
const c = @import("ffi.zig").c;
const gl = @import("gl.zig");
const glutil = @import("glutil.zig");
const GlState = @import("GlState.zig");
pub const Character = struct {
tex_id: c_uint,
size: zalgebra.Vec2_i32,
/// Offset from the left/top
bearing: zalgebra.Vec2_i32,
advance: c_long,
};
pub const CharMap = std.AutoHashMap(u8, Character);
const font_data = @embedFile("iosevka-regular.ttf");
pub var current_projection_matrix: zalgebra.Mat4 = undefined;
pub fn load(chars: *CharMap, line_height: *c_long) !void {
std.log.info("loading font", .{});
var freetype: c.FT_Library = undefined;
if (c.FT_Init_FreeType(&freetype) != 0) {
return error.FreetypeInit;
}
defer _ = c.FT_Done_FreeType(freetype);
var font_face: c.FT_Face = undefined;
if (c.FT_New_Memory_Face(
freetype,
font_data,
font_data.len,
0,
&font_face,
) != 0) {
return error.FontLoading;
}
defer _ = c.FT_Done_Face(font_face);
if (c.FT_Set_Pixel_Sizes(font_face, 0, 48) != 0) {
return error.SetPixelSizes;
}
line_height.* = (font_face.*.size.*.metrics.ascender -
font_face.*.size.*.metrics.descender) >> 5;
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
var char_idx: u8 = 0;
// TODO: also load some fancy chars
while (char_idx < 255) : (char_idx += 1) {
if (c.FT_Load_Char(font_face, char_idx, c.FT_LOAD_RENDER) != 0) {
std.log.warn("freetype failed to load glyph {c}", .{char_idx});
continue;
}
var texture: c_uint = 0;
gl.genTextures(1, &texture);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.RED,
@intCast(c_int, font_face.*.glyph.*.bitmap.width),
@intCast(c_int, font_face.*.glyph.*.bitmap.rows),
0,
gl.RED,
gl.UNSIGNED_BYTE,
font_face.*.glyph.*.bitmap.buffer,
);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
try chars.put(char_idx, .{
.tex_id = texture,
.size = zalgebra.Vec2_i32.new(
@intCast(i32, font_face.*.glyph.*.bitmap.width),
@intCast(i32, font_face.*.glyph.*.bitmap.rows),
),
.bearing = zalgebra.Vec2_i32.new(
font_face.*.glyph.*.bitmap_left,
font_face.*.glyph.*.bitmap_top,
),
.advance = font_face.*.glyph.*.advance.x,
});
}
}
pub fn initFontGl() !void {
std.log.info("creating font rendering pipeline", .{});
const vert = try glutil.loadShader(@embedFile("font_vert.glsl"), gl.VERTEX_SHADER);
defer gl.deleteShader(vert);
const frag = try glutil.loadShader(@embedFile("font_frag.glsl"), gl.FRAGMENT_SHADER);
defer gl.deleteShader(frag);
const program = gl.createProgram();
gl.attachShader(program, vert);
gl.attachShader(program, frag);
gl.linkProgram(program);
try glutil.checkProgramLinkError(program);
GlState.state.font_shader_program = program;
updateProgramProjectionMatrix(GlState.inital_win_size);
gl.genVertexArrays(1, &GlState.state.font_vao);
gl.genBuffers(1, &GlState.state.font_vbo);
gl.bindBuffer(gl.ARRAY_BUFFER, GlState.state.font_vbo);
gl.bufferData(gl.ARRAY_BUFFER, @sizeOf(f32) * 6 * 4, null, gl.DYNAMIC_DRAW);
gl.bindVertexArray(GlState.state.font_vao);
gl.vertexAttribPointer(0, 4, gl.FLOAT, gl.FALSE, 4 * @sizeOf(f32), null);
gl.enableVertexAttribArray(0);
}
pub fn updateProgramProjectionMatrix(size: zalgebra.Vec2_i32) void {
gl.useProgram(GlState.state.font_shader_program);
current_projection_matrix = zalgebra.Mat4.orthographic(
0.0,
@intToFloat(f32, size.x()),
0.0,
@intToFloat(f32, size.y()),
-1.0,
1.0,
);
gl.uniformMatrix4fv(
gl.getUniformLocation(GlState.state.font_shader_program, "projection"),
1,
gl.FALSE,
// elegant much
&[_]f32{
current_projection_matrix.data[0][0],
current_projection_matrix.data[0][1],
current_projection_matrix.data[0][2],
current_projection_matrix.data[0][3],
current_projection_matrix.data[1][0],
current_projection_matrix.data[1][1],
current_projection_matrix.data[1][2],
current_projection_matrix.data[1][3],
current_projection_matrix.data[2][0],
current_projection_matrix.data[2][1],
current_projection_matrix.data[2][2],
current_projection_matrix.data[2][3],
current_projection_matrix.data[3][0],
current_projection_matrix.data[3][1],
current_projection_matrix.data[3][2],
current_projection_matrix.data[3][3],
},
);
}
pub fn drawString(
s: []const u8,
chars: *const CharMap,
line_height: c_long,
x: f32,
y: f32,
scale: f32,
line_max: f32,
) !void {
gl.useProgram(GlState.state.font_shader_program);
gl.bindVertexArray(GlState.state.font_vao);
var cur_x = x;
var cur_y: f32 = 0.0;
var char_iter = (try std.unicode.Utf8View.init(s)).iterator();
while (char_iter.nextCodepoint()) |uchar| {
const char = if (uchar > std.math.maxInt(u8)) '?' else @intCast(u8, uchar);
const ch = chars.get(char) orelse continue;
const xpos = cur_x + @intToFloat(f32, ch.bearing.x()) * scale;
const ypos = y - (@intToFloat(f32, (ch.size.y() - ch.bearing.y())) + cur_y) * scale;
const w = @intToFloat(f32, ch.size.x()) * scale;
const h = @intToFloat(f32, ch.size.y()) * scale;
// zig fmt: off
const vertices = [_]f32{
xpos, ypos + h, 0.0, 0.0,
xpos, ypos, 0.0, 1.0,
xpos + w, ypos, 1.0, 1.0,
xpos, ypos + h, 0.0, 0.0,
xpos + w, ypos, 1.0, 1.0,
xpos + w, ypos + h, 1.0, 0.0,
};
// zig fmt: on
gl.bindTexture(gl.TEXTURE_2D, ch.tex_id);
gl.bindBuffer(gl.ARRAY_BUFFER, GlState.state.font_vbo);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, vertices.len * @sizeOf(f32), &vertices);
gl.drawArrays(gl.TRIANGLES, 0, vertices.len);
// bit shifting stuff is to convert to pixels
cur_x += @intToFloat(f32, ch.advance >> 6) * scale;
if (cur_x + @intToFloat(f32, ch.size.x()) >= line_max) {
cur_x = x;
cur_y += @intToFloat(f32, line_height) * scale;
}
}
gl.bindTexture(gl.TEXTURE_2D, 0);
}

9
src/font_frag.glsl Normal file
View File

@ -0,0 +1,9 @@
#version 330 core
in vec2 texCoords;
out vec4 color;
uniform sampler2D text;
void main() {
color = vec4(1.0, 1.0, 1.0, texture(text, texCoords).r);
}

10
src/font_vert.glsl Normal file
View File

@ -0,0 +1,10 @@
#version 330 core
layout (location = 0) in vec4 vertex; // <vec2 pos, vec2 tex>
out vec2 texCoords;
uniform mat4 projection;
void main() {
gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);
texCoords = vertex.zw;
}

8
src/frag.glsl Normal file
View File

@ -0,0 +1,8 @@
#version 330 core
in vec4 color;
out vec4 fragColor;
void main() {
fragColor = color;
}

5055
src/gl.zig Normal file

File diff suppressed because it is too large Load Diff

82
src/glutil.zig Normal file
View File

@ -0,0 +1,82 @@
const std = @import("std");
const c = @import("ffi.zig").c;
const gl = @import("gl.zig");
const Color = @import("Color.zig");
const GlState = @import("GlState.zig");
pub fn rectangle(
vertex_buf: *std.ArrayList(f32),
x1: f32,
y1: f32,
x2: f32,
y2: f32,
z: f32,
color: Color,
) !void {
// TODO: indexed drawing?
// zig fmt: off
const vertices = [_]f32{
x1, y1, z, color.r, color.g, color.b, color.a,
x1, y2, z, color.r, color.g, color.b, color.a,
x2, y1, z, color.r, color.g, color.b, color.a,
x1, y2, z, color.r, color.g, color.b, color.a,
x2, y1, z, color.r, color.g, color.b, color.a,
x2, y2, z, color.r, color.g, color.b, color.a,
};
// zig fmt: on
try vertex_buf.appendSlice(&vertices);
}
pub fn copyVertices(vertices: []const f32) void {
gl.bindBuffer(gl.ARRAY_BUFFER, GlState.state.vbo);
gl.bufferData(
gl.ARRAY_BUFFER,
@intCast(isize, vertices.len * @sizeOf(f32)),
vertices.ptr,
gl.DYNAMIC_DRAW,
);
}
pub fn drawTriangles(vertices: []const f32) void {
gl.useProgram(GlState.state.shader_program);
copyVertices(vertices);
gl.bindVertexArray(GlState.state.vao);
gl.drawArrays(gl.TRIANGLES, 0, @intCast(c_int, vertices.len / 3));
}
pub fn loadShader(src: [:0]const u8, t: c_uint) !c_uint {
const id = gl.createShader(t);
gl.shaderSource(
id,
1,
&[_][*:0]const u8{src},
&[_]c_int{@intCast(c_int, src.len)},
);
gl.compileShader(id);
try checkShaderCompileError(id);
return id;
}
pub fn checkShaderCompileError(shader: c_uint) !void {
var success: c_int = 0;
gl.getShaderiv(shader, gl.COMPILE_STATUS, &success);
if (success == 0) {
var info_buf = std.mem.zeroes([512:0]u8);
gl.getShaderInfoLog(shader, 512, null, &info_buf);
std.log.err("compiling shader: {s}", .{&info_buf});
return error.ShaderCompileError;
}
}
pub fn checkProgramLinkError(program: c_uint) !void {
var success: c_int = 0;
gl.getProgramiv(program, gl.LINK_STATUS, &success);
if (success == 0) {
var info_buf = std.mem.zeroes([512:0]u8);
gl.getProgramInfoLog(program, 512, null, &info_buf);
std.log.err("linking program: {s}", .{&info_buf});
return error.ProgramError;
}
}

223
src/gui.zig Normal file
View File

@ -0,0 +1,223 @@
const std = @import("std");
const zalgebra = @import("zalgebra");
const c = @import("ffi.zig").c;
const gl = @import("gl.zig");
const glutil = @import("glutil.zig");
const Color = @import("Color.zig");
const GlState = @import("GlState.zig");
const font = @import("font.zig");
// TODO: make these command line params
const width = 4;
const height = 4;
const square_margin = 0.1;
pub const State = struct {
vertex_buf: std.ArrayList(f32),
squares: [][]const u8,
checked: []bool,
chars: font.CharMap,
line_height: c_long,
prev_mouse_state: c_int = 0,
pub fn init(squares: [][]const u8, chars: font.CharMap, line_height: c_long) !State {
return .{
.vertex_buf = std.ArrayList(f32).init(std.heap.c_allocator),
.squares = squares,
.checked = try std.heap.c_allocator.alloc(bool, squares.len),
.chars = chars,
.line_height = line_height,
};
}
pub fn deinit(self: *State) void {
self.vertex_buf.deinit();
std.heap.c_allocator.free(self.checked);
self.* = undefined;
}
};
pub fn handleInput(win: *c.GLFWwindow, state: *State) !void {
if (c.glfwGetKey(win, c.GLFW_KEY_ESCAPE) == c.GLFW_PRESS or
c.glfwGetKey(win, c.GLFW_KEY_Q) == c.GLFW_PRESS)
{
c.glfwSetWindowShouldClose(win, 1);
return;
}
const mouse_state = c.glfwGetMouseButton(win, c.GLFW_MOUSE_BUTTON_LEFT);
if (state.prev_mouse_state != c.GLFW_PRESS and mouse_state == c.GLFW_PRESS) {
std.log.debug("click", .{});
var mousex: f64 = 0;
var mousey: f64 = 0;
c.glfwGetCursorPos(win, &mousex, &mousey);
var norm_cursor_pos = font.current_projection_matrix.mulByVec4(
zalgebra.Vec4.new(
@floatCast(f32, mousex),
@floatCast(f32, mousey),
0.0,
1.0,
),
);
norm_cursor_pos.data[1] = -norm_cursor_pos.y();
const square_width = 2.0 / @intToFloat(f32, width);
const square_height = 2.0 / @intToFloat(f32, height);
blk: {
var x: u32 = 0;
while (x < width) : (x += 1) {
var y: u32 = 0;
while (y < width) : (y += 1) {
const square_x1 = ((@intToFloat(f32, x) + square_margin) / 2.0) - 1.0;
const square_y1 = ((@intToFloat(f32, y) + square_margin) / 2.0) - 1.0;
const square_x2 = square_x1 + square_width - square_margin;
const square_y2 = square_y1 + square_height - square_margin;
if (norm_cursor_pos.x() >= square_x1 and
norm_cursor_pos.x() <= square_x2 and
norm_cursor_pos.y() >= square_y1 and
norm_cursor_pos.y() <= square_y2)
{
state.checked[y * width + x] = !state.checked[y * width + x];
break :blk;
}
}
}
}
}
if (state.prev_mouse_state != mouse_state) {
state.prev_mouse_state = mouse_state;
}
}
pub fn draw(win: *c.GLFWwindow, state: *State) !void {
_ = win;
state.vertex_buf.clearRetainingCapacity();
gl.clearColor(
Color.background.r,
Color.background.g,
Color.background.b,
Color.background.a,
);
gl.clear(gl.COLOR_BUFFER_BIT);
const square_width = 2.0 / @intToFloat(f32, width);
const square_height = 2.0 / @intToFloat(f32, height);
// square frame
{
const color =
Color{
.r = Color.square_frame.r,
.g = Color.square_frame.g,
.b = Color.square_frame.b,
.a = @sin(
@floatCast(f32, c.glfwGetTime()),
) / 2.0 + 0.5,
};
var x: u32 = 0;
while (x < width) : (x += 1) {
var y: u32 = 0;
while (y < width) : (y += 1) {
if (!state.checked[y * width + x])
continue;
const square_x = ((@intToFloat(f32, x) + square_margin - 0.02) / 2.0) -
1.0;
const square_y = ((@intToFloat(f32, y) + square_margin - 0.02) / 2.0) -
1.0;
try glutil.rectangle(
&state.vertex_buf,
square_x,
square_y,
square_x + square_width - square_margin + 0.02,
square_y + square_height - square_margin + 0.02,
-1.0,
color,
);
}
}
}
// squares
{
var x: u32 = 0;
while (x < width) : (x += 1) {
var y: u32 = 0;
while (y < width) : (y += 1) {
const square_x = ((@intToFloat(f32, x) + square_margin) / 2.0) - 1.0;
const square_y = ((@intToFloat(f32, y) + square_margin) / 2.0) - 1.0;
try glutil.rectangle(
&state.vertex_buf,
square_x,
square_y,
square_x + square_width - square_margin,
square_y + square_height - square_margin,
0.0,
Color.square_background,
);
}
}
}
glutil.drawTriangles(state.vertex_buf.items);
// Text
{
const scale = 0.4;
var x: u32 = 0;
while (x < width) : (x += 1) {
var y: u32 = 0;
while (y < width) : (y += 1) {
const pos = zalgebra.Vec4.new(
((@intToFloat(f32, x) + square_margin) / 2.0) - 1.0,
((@intToFloat(f32, y) + square_margin) / 2.0) - 1.0,
0.0,
1.0,
).add(
zalgebra.Vec4.new(
0.0,
square_height - square_margin,
0.0,
0.0,
),
);
const inv_proj_mtx = font.current_projection_matrix.inv();
const screenpos = inv_proj_mtx.mulByVec4(pos).sub(
zalgebra.Vec4.new(0.0, 48.0 * scale, 0.0, 0.0),
);
const square_size = inv_proj_mtx.mulByVec4(
pos.add(zalgebra.Vec4.new(
square_width - square_margin,
square_height - square_margin,
0.0,
0.0,
)),
);
try font.drawString(
state.squares[y * width + x],
&state.chars,
state.line_height,
screenpos.x(),
screenpos.y(),
scale,
square_size.x(),
);
}
}
}
}

BIN
src/iosevka-regular.ttf Normal file

Binary file not shown.

153
src/main.zig Normal file
View File

@ -0,0 +1,153 @@
const std = @import("std");
const zalgebra = @import("zalgebra");
const c = @import("ffi.zig").c;
const gl = @import("gl.zig");
const glutil = @import("glutil.zig");
const gui = @import("gui.zig");
const GlState = @import("GlState.zig");
const bingoloader = @import("bingoloader.zig");
const font = @import("font.zig");
pub fn main() !void {
if (std.os.argv.len != 2) {
std.log.err(
\\Invalid CLI usage
\\
\\Usage:
\\{s} [bingolist]
, .{std.os.argv[0]});
return error.InvalidCLI;
}
var bingos = try bingoloader.loadFile(std.mem.span(std.os.argv[1]), 16);
defer bingos.deinit();
std.log.info("initializing GL state", .{});
try GlState.init();
defer GlState.deinit();
std.log.info("initializing GLFW", .{});
if (c.glfwInit() == c.GLFW_FALSE) {
return error.GlfwInit;
}
defer c.glfwTerminate();
c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MAJOR, 3);
c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MINOR, 3);
c.glfwWindowHint(c.GLFW_OPENGL_PROFILE, c.GLFW_OPENGL_CORE_PROFILE);
// fancy transparent window
c.glfwWindowHint(c.GLFW_TRANSPARENT_FRAMEBUFFER, c.GLFW_TRUE);
std.log.info("creating window", .{});
const win = c.glfwCreateWindow(
GlState.inital_win_size.x(),
GlState.inital_win_size.y(),
"Bingus",
null,
null,
);
if (win == null) {
return error.GlfwCreateWindow;
}
// create OpenGL context
c.glfwMakeContextCurrent(win);
std.log.info("loading OpenGL", .{});
try gl.load({}, getProcAddress);
// VSync
c.glfwSwapInterval(1);
// window resize callback
_ = c.glfwSetFramebufferSizeCallback(win, &fbResizeCb);
// mouse button buffering
c.glfwSetInputMode(win, c.GLFW_STICKY_MOUSE_BUTTONS, c.GLFW_TRUE);
gl.viewport(0, 0, GlState.inital_win_size.x(), GlState.inital_win_size.y());
std.log.info("loading shaders", .{});
const vert_shader = try glutil.loadShader(
@embedFile("vert.glsl"),
gl.VERTEX_SHADER,
);
const frag_shader = try glutil.loadShader(
@embedFile("frag.glsl"),
gl.FRAGMENT_SHADER,
);
const shader_program = gl.createProgram();
gl.attachShader(shader_program, vert_shader);
gl.attachShader(shader_program, frag_shader);
gl.linkProgram(shader_program);
try glutil.checkProgramLinkError(shader_program);
GlState.state.shader_program = shader_program;
// shaders can be delted once linked to a program
gl.deleteShader(vert_shader);
gl.deleteShader(frag_shader);
gl.genVertexArrays(1, &GlState.state.vao);
gl.genBuffers(1, &GlState.state.vbo);
gl.bindVertexArray(GlState.state.vao);
gl.bindBuffer(gl.ARRAY_BUFFER, GlState.state.vbo);
// vertex pos
gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 7 * @sizeOf(f32), null);
gl.enableVertexAttribArray(0);
// vertex color
gl.vertexAttribPointer(
1,
4,
gl.FLOAT,
gl.FALSE,
7 * @sizeOf(f32),
@intToPtr(*anyopaque, 3 * @sizeOf(f32)),
);
gl.enableVertexAttribArray(1);
try font.initFontGl();
//gl.enable(gl.CULL_FACE);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
var chars = font.CharMap.init(std.heap.c_allocator);
defer chars.deinit();
var line_height: c_long = 0;
try font.load(&chars, &line_height);
//gl.polygonMode(gl.FRONT_AND_BACK, gl.LINE);
var gui_state = try gui.State.init(bingos.bingos, chars, line_height);
defer gui_state.deinit();
while (c.glfwWindowShouldClose(win) == 0) {
c.glfwPollEvents();
defer c.glfwSwapBuffers(win);
try gui.handleInput(win.?, &gui_state);
if (c.glfwGetWindowAttrib(win, c.GLFW_VISIBLE) == 0) {
// window isn't visible, so no need to do any rendering
continue;
}
try gui.draw(win.?, &gui_state);
}
}
fn getProcAddress(_: void, name: [:0]const u8) ?gl.FunctionPointer {
return c.glfwGetProcAddress(name.ptr);
}
fn fbResizeCb(win: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void {
_ = win;
const vec = zalgebra.Vec2_i32.new(width, height);
gl.viewport(0, 0, width, height);
font.updateProgramProjectionMatrix(vec);
GlState.state.current_screen_size = vec;
}

11
src/vert.glsl Normal file
View File

@ -0,0 +1,11 @@
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec4 vColor;
out vec4 color;
void main() {
gl_Position = vec4(aPos, 1.0);
color = vColor;
}

1
zalgebra Submodule

@ -0,0 +1 @@
Subproject commit 3c246c622598417c849a5c7425b8df211bb443b3