const std = @import("std"); const c = @import("c.zig").c; // opengl constants const GL_COLOR_BUFFER_BIT = 0x00004000; const GL_DEPTH_BUFFER_BIT = 0x00000100; const GL_TEXTURE_2D = 0x0DE1; const GL_RGB = 0x1907; const GL_BGRA = 0x80E1; const GL_UNSIGNED_BYTE = 0x1401; const GL_NEAREST = 0x2600; const GL_LINEAR = 0x2601; const GL_CLAMP_TO_BORDER = 0x812D; const GL_TEXTURE_MIN_FILTER = 0x2801; const GL_TEXTURE_MAG_FILTER = 0x2800; const GL_TEXTURE_WRAP_S = 0x2802; const GL_TEXTURE_WRAP_T = 0x2803; const GL_ARRAY_BUFFER = 0x8892; const GL_ELEMENT_ARRAY_BUFFER = 0x8893; const GL_STATIC_DRAW = 0x88E4; const GL_FLOAT = 0x1406; const GL_VERTEX_SHADER = 0x8B31; const GL_FRAGMENT_SHADER = 0x8B30; const GL_COMPILE_STATUS = 0x8B81; const GL_LINK_STATUS = 0x8B82; const GL_TEXTURE0 = 0x84C0; const GL_TRIANGLES = 0x0004; const GL_UNSIGNED_INT = 0x1405; const GL_TRUE = 1; extern fn glewInit() c_uint; extern fn glewGetErrorString(err: c_uint) [*c]const u8; extern fn glewGetString(name: c_uint) [*c]const u8; extern fn glEnable(cap: c_uint) void; extern fn glClearColor(r: f32, g: f32, b: f32, a: f32) void; extern fn glClear(mask: c_uint) void; extern fn glViewport(x: i32, y: i32, w: i32, h: i32) void; extern fn glFinish() void; extern fn glCreateShader(type_: c_uint) c_uint; extern fn glShaderSource(shader: c_uint, count: i32, string: [*c]const [*c]const u8, length: [*c]const i32) void; extern fn glCompileShader(shader: c_uint) void; extern fn glGetShaderiv(shader: c_uint, pname: c_uint, params: [*c]i32) void; extern fn glGetShaderInfoLog(shader: c_uint, buf_size: i32, length: [*c]i32, info_log: [*c]u8) void; extern fn glDeleteShader(shader: c_uint) void; extern fn glCreateProgram() c_uint; extern fn glAttachShader(program: c_uint, shader: c_uint) void; extern fn glLinkProgram(program: c_uint) void; extern fn glGetProgramiv(program: c_uint, pname: c_uint, params: [*c]i32) void; extern fn glGetProgramInfoLog(program: c_uint, buf_size: i32, length: [*c]i32, info_log: [*c]u8) void; extern fn glDeleteProgram(program: c_uint) void; extern fn glUseProgram(program: c_uint) void; extern fn glGetUniformLocation(program: c_uint, name: [*c]const u8) i32; extern fn glUniform1i(location: i32, v0: i32) void; extern fn glUniform1f(location: i32, v0: f32) void; extern fn glUniform2f(location: i32, v0: f32, v1: f32) void; extern fn glGenTextures(n: i32, textures: [*c]c_uint) void; extern fn glActiveTexture(texture: c_uint) void; extern fn glBindTexture(target: c_uint, texture: c_uint) void; extern fn glTexImage2D(target: c_uint, level: i32, internalformat: i32, width: i32, height: i32, border: i32, format: c_uint, type_: c_uint, pixels: ?*const anyopaque) void; extern fn glGenerateMipmap(target: c_uint) void; extern fn glTexParameteri(target: c_uint, pname: c_uint, param: i32) void; extern fn glDeleteTextures(n: i32, textures: [*c]const c_uint) void; extern fn glGenVertexArrays(n: i32, arrays: [*c]c_uint) void; extern fn glGenBuffers(n: i32, buffers: [*c]c_uint) void; extern fn glBindVertexArray(array: c_uint) void; extern fn glBindBuffer(target: c_uint, buffer: c_uint) void; extern fn glBufferData(target: c_uint, size: isize, data: ?*const anyopaque, usage: c_uint) void; extern fn glVertexAttribPointer(index: c_uint, size: i32, type_: c_uint, normalized: u8, stride: i32, pointer: ?*const anyopaque) void; extern fn glEnableVertexAttribArray(index: c_uint) void; extern fn glDrawElements(mode: c_uint, count: i32, type_: c_uint, indices: ?*const anyopaque) void; extern fn glDeleteVertexArrays(n: i32, arrays: [*c]const c_uint) void; extern fn glDeleteBuffers(n: i32, buffers: [*c]const c_uint) void; const vertex_shader_source = \\#version 130 \\in vec3 aPos; \\in vec2 aTexCoord; \\out vec2 texcoord; \\uniform vec2 camera_pos; \\uniform float camera_scale; \\uniform vec2 window_size; \\uniform vec2 screenshot_size; \\vec3 to_world(vec3 v) { \\ vec2 ratio = vec2( \\ window_size.x / screenshot_size.x / camera_scale, \\ window_size.y / screenshot_size.y / camera_scale); \\ return vec3((v.x / screenshot_size.x * 2.0 - 1.0) / ratio.x, \\ (v.y / screenshot_size.y * 2.0 - 1.0) / ratio.y, \\ v.z); \\} \\void main() { \\ gl_Position = vec4(to_world((aPos - vec3(camera_pos * vec2(1.0, -1.0), 0.0))), 1.0); \\ texcoord = aTexCoord; \\} ; const fragment_shader_source = \\#version 130 \\out mediump vec4 color; \\in mediump vec2 texcoord; \\uniform sampler2D tex; \\uniform vec2 cursor_pos; \\uniform vec2 window_size; \\uniform float fl_shadow; \\uniform float fl_radius; \\uniform float camera_scale; \\uniform float fl_feather; \\uniform float mirror; \\void main() { \\ vec4 cursor = vec4(cursor_pos.x, window_size.y - cursor_pos.y, 0.0, 1.0); \\ float dist = length(cursor - gl_FragCoord); \\ float radius_px = fl_radius * camera_scale; \\ float inner = radius_px * (1.0 - fl_feather); \\ float outer = radius_px; \\ float alpha = smoothstep(inner, outer, dist); \\ vec2 tc = texcoord; \\ if (mirror > 0.5) tc.x = 1.0 - tc.x; \\ color = mix(texture(tex, tc), vec4(0.0, 0.0, 0.0, 0.0), alpha * fl_shadow); \\} ; pub const GL = struct { program: c_uint = 0, texture: c_uint = 0, vao: c_uint = 0, vbo: c_uint = 0, ebo: c_uint = 0, screenshot_w: i32 = 0, screenshot_h: i32 = 0, pub fn init(screenshot: anytype) !GL { const err = glewInit(); if (err != c.GLEW_OK) { std.debug.print("error: glew: {s}\n", .{glewGetErrorString(err)}); return error.GlewInitFailed; } std.debug.print("glew: {s}\n", .{glewGetString(c.GLEW_VERSION)}); glEnable(GL_TEXTURE_2D); return .{ .screenshot_w = screenshot.image.*.width, .screenshot_h = screenshot.image.*.height, }; } pub fn createProgram(self: *GL) void { const vertex = compileShader(GL_VERTEX_SHADER, vertex_shader_source); const fragment = compileShader(GL_FRAGMENT_SHADER, fragment_shader_source); self.program = glCreateProgram(); glAttachShader(self.program, vertex); glAttachShader(self.program, fragment); glLinkProgram(self.program); var success: i32 = 0; glGetProgramiv(self.program, GL_LINK_STATUS, &success); if (success == 0) { var log: [512]u8 = undefined; glGetProgramInfoLog(self.program, 512, null, &log); std.debug.print("error: program link: {s}\n", .{log}); return; } glDeleteShader(vertex); glDeleteShader(fragment); glUseProgram(self.program); glUniform1i(glGetUniformLocation(self.program, "tex"), 0); } pub fn createTexture(self: *GL, screenshot: anytype, filter: i32) void { glGenTextures(1, &self.texture); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, self.texture); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, screenshot.image.*.width, screenshot.image.*.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, @ptrCast(screenshot.image.*.data), ); glGenerateMipmap(GL_TEXTURE_2D); if (filter != 0) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); } pub fn createGeometry(self: *GL) void { const vertices = [_]f32{ @floatFromInt(self.screenshot_w), 0, 0.0, 1.0, 1.0, @floatFromInt(self.screenshot_w), @floatFromInt(self.screenshot_h), 0.0, 1.0, 0.0, 0, @floatFromInt(self.screenshot_h), 0.0, 0.0, 0.0, 0, 0, 0.0, 0.0, 1.0, }; const indices = [_]c_uint{ 0, 1, 3, 1, 2, 3 }; glGenVertexArrays(1, &self.vao); glGenBuffers(1, &self.vbo); glGenBuffers(1, &self.ebo); glBindVertexArray(self.vao); glBindBuffer(GL_ARRAY_BUFFER, self.vbo); glBufferData(GL_ARRAY_BUFFER, @sizeOf(@TypeOf(vertices)), &vertices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.ebo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, @sizeOf(@TypeOf(indices)), &indices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, 0, 5 * @sizeOf(f32), null); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 2, GL_FLOAT, 0, 5 * @sizeOf(f32), @ptrFromInt(3 * @sizeOf(f32))); glEnableVertexAttribArray(1); } pub fn render(self: *GL, app: anytype, ww: i32, wh: i32) void { glClearColor(0.1, 0.1, 0.1, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glUseProgram(self.program); const cam = &app.state.camera; const fl = &app.state.flashlight; const mouse = &app.state.mouse; glUniform2f( glGetUniformLocation(self.program, "camera_pos"), cam.position.x, cam.position.y, ); glUniform1f(glGetUniformLocation(self.program, "camera_scale"), cam.scale); glUniform2f(glGetUniformLocation(self.program, "window_size"), @floatFromInt(ww), @floatFromInt(wh)); glUniform2f( glGetUniformLocation(self.program, "screenshot_size"), @floatFromInt(self.screenshot_w), @floatFromInt(self.screenshot_h), ); glUniform2f( glGetUniformLocation(self.program, "cursor_pos"), mouse.curr.x, mouse.curr.y, ); glUniform1f(glGetUniformLocation(self.program, "fl_shadow"), fl.shadow); glUniform1f(glGetUniformLocation(self.program, "fl_radius"), fl.radius); glUniform1f(glGetUniformLocation(self.program, "fl_feather"), app.config.feather); glUniform1f(glGetUniformLocation(self.program, "mirror"), @floatFromInt(@intFromBool(app.state.mirror))); glBindTexture(GL_TEXTURE_2D, self.texture); glBindVertexArray(self.vao); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, null); } pub fn deinit(self: *GL) void { if (self.vao != 0) glDeleteVertexArrays(1, &self.vao); if (self.vbo != 0) glDeleteBuffers(1, &self.vbo); if (self.ebo != 0) glDeleteBuffers(1, &self.ebo); if (self.program != 0) glDeleteProgram(self.program); if (self.texture != 0) glDeleteTextures(1, &self.texture); } }; fn compileShader(type_: c_uint, source: [:0]const u8) c_uint { const shader = glCreateShader(type_); const src_ptr: [*c]const u8 = source.ptr; const src_len: i32 = @intCast(source.len); glShaderSource(shader, 1, &src_ptr, &src_len); glCompileShader(shader); var success: i32 = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &success); if (success == 0) { var log: [512]u8 = undefined; glGetShaderInfoLog(shader, 512, null, &log); std.debug.print("error: shader compile: {s}\n", .{log}); } return shader; }