const std = @import("std"); const c_allocator = @import("std").heap.c_allocator; const c = @import("c.zig"); const cfg = @import("config.zig"); const UniformType = enum(c.GLint) { FLOAT = c.GL_FLOAT, FLOAT_VEC2 = c.GL_FLOAT_VEC2, FLOAT_VEC3 = c.GL_FLOAT_VEC3, FLOAT_VEC4 = c.GL_FLOAT_VEC4, DOUBLE = c.GL_DOUBLE, DOUBLE_VEC2 = c.GL_DOUBLE_VEC2, DOUBLE_VEC3 = c.GL_DOUBLE_VEC3, DOUBLE_VEC4 = c.GL_DOUBLE_VEC4, INT = c.GL_INT, INT_VEC2 = c.GL_INT_VEC2, INT_VEC3 = c.GL_INT_VEC3, INT_VEC4 = c.GL_INT_VEC4, UNSIGNED_INT = c.GL_UNSIGNED_INT, UNSIGNED_INT_VEC2 = c.GL_UNSIGNED_INT_VEC2, UNSIGNED_INT_VEC3 = c.GL_UNSIGNED_INT_VEC3, UNSIGNED_INT_VEC4 = c.GL_UNSIGNED_INT_VEC4, BOOL = c.GL_BOOL, BOOL_VEC2 = c.GL_BOOL_VEC2, BOOL_VEC3 = c.GL_BOOL_VEC3, BOOL_VEC4 = c.GL_BOOL_VEC4, FLOAT_MAT2 = c.GL_FLOAT_MAT2, FLOAT_MAT3 = c.GL_FLOAT_MAT3, FLOAT_MAT4 = c.GL_FLOAT_MAT4, FLOAT_MAT2x3 = c.GL_FLOAT_MAT2x3, FLOAT_MAT2x4 = c.GL_FLOAT_MAT2x4, FLOAT_MAT3x2 = c.GL_FLOAT_MAT3x2, FLOAT_MAT3x4 = c.GL_FLOAT_MAT3x4, FLOAT_MAT4x2 = c.GL_FLOAT_MAT4x2, FLOAT_MAT4x3 = c.GL_FLOAT_MAT4x3, DOUBLE_MAT2 = c.GL_DOUBLE_MAT2, DOUBLE_MAT3 = c.GL_DOUBLE_MAT3, DOUBLE_MAT4 = c.GL_DOUBLE_MAT4, DOUBLE_MAT2x3 = c.GL_DOUBLE_MAT2x3, DOUBLE_MAT2x4 = c.GL_DOUBLE_MAT2x4, DOUBLE_MAT3x2 = c.GL_DOUBLE_MAT3x2, DOUBLE_MAT3x4 = c.GL_DOUBLE_MAT3x4, DOUBLE_MAT4x2 = c.GL_DOUBLE_MAT4x2, DOUBLE_MAT4x3 = c.GL_DOUBLE_MAT4x3, SAMPLER_1D = c.GL_SAMPLER_1D, SAMPLER_2D = c.GL_SAMPLER_2D, SAMPLER_3D = c.GL_SAMPLER_3D, SAMPLER_CUBE = c.GL_SAMPLER_CUBE, SAMPLER_1D_SHADOW = c.GL_SAMPLER_1D_SHADOW, SAMPLER_2D_SHADOW = c.GL_SAMPLER_2D_SHADOW, SAMPLER_1D_ARRAY = c.GL_SAMPLER_1D_ARRAY, SAMPLER_2D_ARRAY = c.GL_SAMPLER_2D_ARRAY, SAMPLER_1D_ARRAY_SHADOW = c.GL_SAMPLER_1D_ARRAY_SHADOW, SAMPLER_2D_ARRAY_SHADOW = c.GL_SAMPLER_2D_ARRAY_SHADOW, SAMPLER_2D_MULTISAMPLE = c.GL_SAMPLER_2D_MULTISAMPLE, SAMPLER_2D_MULTISAMPLE_ARRAY = c.GL_SAMPLER_2D_MULTISAMPLE_ARRAY, SAMPLER_CUBE_SHADOW = c.GL_SAMPLER_CUBE_SHADOW, SAMPLER_BUFFER = c.GL_SAMPLER_BUFFER, SAMPLER_2D_RECT = c.GL_SAMPLER_2D_RECT, SAMPLER_2D_RECT_SHADOW = c.GL_SAMPLER_2D_RECT_SHADOW, INT_SAMPLER_1D = c.GL_INT_SAMPLER_1D, INT_SAMPLER_2D = c.GL_INT_SAMPLER_2D, INT_SAMPLER_3D = c.GL_INT_SAMPLER_3D, INT_SAMPLER_CUBE = c.GL_INT_SAMPLER_CUBE, INT_SAMPLER_1D_ARRAY = c.GL_INT_SAMPLER_1D_ARRAY, INT_SAMPLER_2D_ARRAY = c.GL_INT_SAMPLER_2D_ARRAY, INT_SAMPLER_2D_MULTISAMPLE = c.GL_INT_SAMPLER_2D_MULTISAMPLE, INT_SAMPLER_2D_MULTISAMPLE_ARRAY = c.GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY, INT_SAMPLER_BUFFER = c.GL_INT_SAMPLER_BUFFER, INT_SAMPLER_2D_RECT = c.GL_INT_SAMPLER_2D_RECT, UNSIGNED_INT_SAMPLER_1D = c.GL_UNSIGNED_INT_SAMPLER_1D, UNSIGNED_INT_SAMPLER_2D = c.GL_UNSIGNED_INT_SAMPLER_2D, UNSIGNED_INT_SAMPLER_3D = c.GL_UNSIGNED_INT_SAMPLER_3D, UNSIGNED_INT_SAMPLER_CUBE = c.GL_UNSIGNED_INT_SAMPLER_CUBE, UNSIGNED_INT_SAMPLER_1D_ARRAY = c.GL_UNSIGNED_INT_SAMPLER_1D_ARRAY, UNSIGNED_INT_SAMPLER_2D_ARRAY = c.GL_UNSIGNED_INT_SAMPLER_2D_ARRAY, UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE = c.GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE, UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY = c.GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY, UNSIGNED_INT_SAMPLER_BUFFER = c.GL_UNSIGNED_INT_SAMPLER_BUFFER, UNSIGNED_INT_SAMPLER_2D_RECT = c.GL_UNSIGNED_INT_SAMPLER_2D_RECT, }; const UniformValue = union(UniformType) { FLOAT: f32, FLOAT_VEC2: [2]f32, FLOAT_VEC3: [3]f32, FLOAT_VEC4: [4]f32, DOUBLE: f64, DOUBLE_VEC2: [2]f64, DOUBLE_VEC3: [3]f64, DOUBLE_VEC4: [4]f64, INT: c_int, // i32 but bug INT_VEC2: [2]c_int, INT_VEC3: [3]c_int, INT_VEC4: [4]c_int, UNSIGNED_INT: c_uint, UNSIGNED_INT_VEC2: [2]c_uint, UNSIGNED_INT_VEC3: [3]c_uint, UNSIGNED_INT_VEC4: [4]c_uint, BOOL: c_uint, BOOL_VEC2: [2]c_uint, BOOL_VEC3: [3]c_uint, BOOL_VEC4: [4]c_uint, FLOAT_MAT2: [2 * 2]f32, FLOAT_MAT3: [3 * 3]f32, FLOAT_MAT4: [4 * 4]f32, FLOAT_MAT2x3: [2 * 3]f32, FLOAT_MAT2x4: [2 * 4]f32, FLOAT_MAT3x2: [3 * 2]f32, FLOAT_MAT3x4: [3 * 4]f32, FLOAT_MAT4x2: [4 * 2]f32, FLOAT_MAT4x3: [4 * 3]f32, DOUBLE_MAT2: [2 * 2]f64, DOUBLE_MAT3: [3 * 3]f64, DOUBLE_MAT4: [4 * 4]f64, DOUBLE_MAT2x3: [2 * 3]f64, DOUBLE_MAT2x4: [2 * 4]f64, DOUBLE_MAT3x2: [3 * 2]f64, DOUBLE_MAT3x4: [3 * 4]f64, DOUBLE_MAT4x2: [4 * 2]f64, DOUBLE_MAT4x3: [4 * 3]f64, SAMPLER_1D: c_int, SAMPLER_2D: c_int, SAMPLER_3D: c_int, SAMPLER_CUBE: c_int, SAMPLER_1D_SHADOW: c_int, SAMPLER_2D_SHADOW: c_int, SAMPLER_1D_ARRAY: c_int, SAMPLER_2D_ARRAY: c_int, SAMPLER_1D_ARRAY_SHADOW: c_int, SAMPLER_2D_ARRAY_SHADOW: c_int, SAMPLER_2D_MULTISAMPLE: c_int, SAMPLER_2D_MULTISAMPLE_ARRAY: c_int, SAMPLER_CUBE_SHADOW: c_int, SAMPLER_BUFFER: c_int, SAMPLER_2D_RECT: c_int, SAMPLER_2D_RECT_SHADOW: c_int, INT_SAMPLER_1D: c_int, INT_SAMPLER_2D: c_int, INT_SAMPLER_3D: c_int, INT_SAMPLER_CUBE: c_int, INT_SAMPLER_1D_ARRAY: c_int, INT_SAMPLER_2D_ARRAY: c_int, INT_SAMPLER_2D_MULTISAMPLE: c_int, INT_SAMPLER_2D_MULTISAMPLE_ARRAY: c_int, INT_SAMPLER_BUFFER: c_int, INT_SAMPLER_2D_RECT: c_int, UNSIGNED_INT_SAMPLER_1D: c_int, UNSIGNED_INT_SAMPLER_2D: c_int, UNSIGNED_INT_SAMPLER_3D: c_int, UNSIGNED_INT_SAMPLER_CUBE: c_int, UNSIGNED_INT_SAMPLER_1D_ARRAY: c_int, UNSIGNED_INT_SAMPLER_2D_ARRAY: c_int, UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: c_int, UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: c_int, UNSIGNED_INT_SAMPLER_BUFFER: c_int, UNSIGNED_INT_SAMPLER_2D_RECT: c_int, pub fn init(utype: UniformType) UniformValue { inline for (@typeInfo(UniformType).Enum.fields) |field| { if (@intToEnum(UniformType, field.value) == utype) return @unionInit(UniformValue, field.name, undefined); } unreachable; } }; const CachedUniform = struct { value: UniformValue, location: c.GLint, pub fn init(shader: ShaderProgram, name: [*]const u8) !CachedUniform { var index: c.GLuint = undefined; c.glGetUniformIndices(shader.program_id, 1, &name, &index); if (index == c.GL_INVALID_INDEX) return error.invalidUniform; var utype: c.GLint = undefined; c.glGetActiveUniformsiv(shader.program_id, 1, &index, c.GL_UNIFORM_TYPE, &utype); var self = CachedUniform{ .value = UniformValue.init(@intToEnum(UniformType, utype)), .location = try shader.uniformLocation(name), }; self.getShaderValue(shader); return self; } // zig fmt: off pub fn setShaderValue(self: *const CachedUniform, shader: ShaderProgram) void { const program = shader.program_id; const location = self.location; switch (self.value) { .FLOAT => |*value| c.glProgramUniform1fv(program, location, 1, value), .FLOAT_VEC2 => |*value| c.glProgramUniform2fv(program, location, 1, value), .FLOAT_VEC3 => |*value| c.glProgramUniform3fv(program, location, 1, value), .FLOAT_VEC4 => |*value| c.glProgramUniform4fv(program, location, 1, value), .DOUBLE => |*value| c.glProgramUniform1dv(program, location, 1, value), .DOUBLE_VEC2 => |*value| c.glProgramUniform1dv(program, location, 1, value), .DOUBLE_VEC3 => |*value| c.glProgramUniform2dv(program, location, 1, value), .DOUBLE_VEC4 => |*value| c.glProgramUniform3dv(program, location, 1, value), .INT => |*value| c.glProgramUniform1iv(program, location, 1, value), .INT_VEC2 => |*value| c.glProgramUniform2iv(program, location, 1, value), .INT_VEC3 => |*value| c.glProgramUniform3iv(program, location, 1, value), .INT_VEC4 => |*value| c.glProgramUniform4iv(program, location, 1, value), .UNSIGNED_INT => |*value| c.glProgramUniform1uiv(program, location, 1, value), .UNSIGNED_INT_VEC2 => |*value| c.glProgramUniform2uiv(program, location, 1, value), .UNSIGNED_INT_VEC3 => |*value| c.glProgramUniform3uiv(program, location, 1, value), .UNSIGNED_INT_VEC4 => |*value| c.glProgramUniform4uiv(program, location, 1, value), .BOOL => |*value| c.glProgramUniform1uiv(program, location, 1, value), .BOOL_VEC2 => |*value| c.glProgramUniform2uiv(program, location, 1, value), .BOOL_VEC3 => |*value| c.glProgramUniform3uiv(program, location, 1, value), .BOOL_VEC4 => |*value| c.glProgramUniform4uiv(program, location, 1, value), .FLOAT_MAT2 => |*value| c.glProgramUniformMatrix2fv(program, location, 1, c.GL_FALSE, value), .FLOAT_MAT3 => |*value| c.glProgramUniformMatrix3fv(program, location, 1, c.GL_FALSE, value), .FLOAT_MAT4 => |*value| c.glProgramUniformMatrix4fv(program, location, 1, c.GL_FALSE, value), .FLOAT_MAT2x3 => |*value| c.glProgramUniformMatrix2x3fv(program, location, 1, c.GL_FALSE, value), .FLOAT_MAT2x4 => |*value| c.glProgramUniformMatrix2x4fv(program, location, 1, c.GL_FALSE, value), .FLOAT_MAT3x2 => |*value| c.glProgramUniformMatrix3x2fv(program, location, 1, c.GL_FALSE, value), .FLOAT_MAT3x4 => |*value| c.glProgramUniformMatrix3x4fv(program, location, 1, c.GL_FALSE, value), .FLOAT_MAT4x2 => |*value| c.glProgramUniformMatrix4x2fv(program, location, 1, c.GL_FALSE, value), .FLOAT_MAT4x3 => |*value| c.glProgramUniformMatrix4x3fv(program, location, 1, c.GL_FALSE, value), .DOUBLE_MAT2 => |*value| c.glProgramUniformMatrix2dv(program, location, 1, c.GL_FALSE, value), .DOUBLE_MAT3 => |*value| c.glProgramUniformMatrix3dv(program, location, 1, c.GL_FALSE, value), .DOUBLE_MAT4 => |*value| c.glProgramUniformMatrix4dv(program, location, 1, c.GL_FALSE, value), .DOUBLE_MAT2x3 => |*value| c.glProgramUniformMatrix2x3dv(program, location, 1, c.GL_FALSE, value), .DOUBLE_MAT2x4 => |*value| c.glProgramUniformMatrix2x4dv(program, location, 1, c.GL_FALSE, value), .DOUBLE_MAT3x2 => |*value| c.glProgramUniformMatrix3x2dv(program, location, 1, c.GL_FALSE, value), .DOUBLE_MAT3x4 => |*value| c.glProgramUniformMatrix3x4dv(program, location, 1, c.GL_FALSE, value), .DOUBLE_MAT4x2 => |*value| c.glProgramUniformMatrix4x2dv(program, location, 1, c.GL_FALSE, value), .DOUBLE_MAT4x3 => |*value| c.glProgramUniformMatrix4x3dv(program, location, 1, c.GL_FALSE, value), .SAMPLER_1D, .SAMPLER_2D, .SAMPLER_3D, .SAMPLER_CUBE, .SAMPLER_1D_SHADOW, .SAMPLER_2D_SHADOW, .SAMPLER_1D_ARRAY, .SAMPLER_2D_ARRAY, .SAMPLER_1D_ARRAY_SHADOW, .SAMPLER_2D_ARRAY_SHADOW, .SAMPLER_2D_MULTISAMPLE, .SAMPLER_2D_MULTISAMPLE_ARRAY, .SAMPLER_CUBE_SHADOW, .SAMPLER_BUFFER, .SAMPLER_2D_RECT, .SAMPLER_2D_RECT_SHADOW, .INT_SAMPLER_1D, .INT_SAMPLER_2D, .INT_SAMPLER_3D, .INT_SAMPLER_CUBE, .INT_SAMPLER_1D_ARRAY, .INT_SAMPLER_2D_ARRAY, .INT_SAMPLER_2D_MULTISAMPLE, .INT_SAMPLER_2D_MULTISAMPLE_ARRAY, .INT_SAMPLER_BUFFER, .INT_SAMPLER_2D_RECT, .UNSIGNED_INT_SAMPLER_1D, .UNSIGNED_INT_SAMPLER_2D, .UNSIGNED_INT_SAMPLER_3D, .UNSIGNED_INT_SAMPLER_CUBE, .UNSIGNED_INT_SAMPLER_1D_ARRAY, .UNSIGNED_INT_SAMPLER_2D_ARRAY, .UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE, .UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY, .UNSIGNED_INT_SAMPLER_BUFFER, .UNSIGNED_INT_SAMPLER_2D_RECT => |value| c.glProgramUniform1i(program, location, value), } } pub fn getShaderValue(self: *CachedUniform, shader: ShaderProgram) void { const program = shader.program_id; const location = self.location; switch (self.value) { .FLOAT => |*value| c.glGetnUniformfv(program, location, @sizeOf(@TypeOf(value.*)), value), .FLOAT_VEC2 => |*value| c.glGetnUniformfv(program, location, @sizeOf(@TypeOf(value.*)), value), .FLOAT_VEC3 => |*value| c.glGetnUniformfv(program, location, @sizeOf(@TypeOf(value.*)), value), .FLOAT_VEC4 => |*value| c.glGetnUniformfv(program, location, @sizeOf(@TypeOf(value.*)), value), .DOUBLE => |*value| c.glGetnUniformdv(program, location, @sizeOf(@TypeOf(value.*)), value), .DOUBLE_VEC2 => |*value| c.glGetnUniformdv(program, location, @sizeOf(@TypeOf(value.*)), value), .DOUBLE_VEC3 => |*value| c.glGetnUniformdv(program, location, @sizeOf(@TypeOf(value.*)), value), .DOUBLE_VEC4 => |*value| c.glGetnUniformdv(program, location, @sizeOf(@TypeOf(value.*)), value), .INT => |*value| c.glGetnUniformiv(program, location, @sizeOf(@TypeOf(value.*)), value), .INT_VEC2 => |*value| c.glGetnUniformiv(program, location, @sizeOf(@TypeOf(value.*)), value), .INT_VEC3 => |*value| c.glGetnUniformiv(program, location, @sizeOf(@TypeOf(value.*)), value), .INT_VEC4 => |*value| c.glGetnUniformiv(program, location, @sizeOf(@TypeOf(value.*)), value), .UNSIGNED_INT => |*value| c.glGetnUniformuiv(program, location, @sizeOf(@TypeOf(value.*)), value), .UNSIGNED_INT_VEC2 => |*value| c.glGetnUniformuiv(program, location, @sizeOf(@TypeOf(value.*)), value), .UNSIGNED_INT_VEC3 => |*value| c.glGetnUniformuiv(program, location, @sizeOf(@TypeOf(value.*)), value), .UNSIGNED_INT_VEC4 => |*value| c.glGetnUniformuiv(program, location, @sizeOf(@TypeOf(value.*)), value), .BOOL => |*value| c.glGetnUniformuiv(program, location, @sizeOf(@TypeOf(value.*)), value), .BOOL_VEC2 => |*value| c.glGetnUniformuiv(program, location, @sizeOf(@TypeOf(value.*)), value), .BOOL_VEC3 => |*value| c.glGetnUniformuiv(program, location, @sizeOf(@TypeOf(value.*)), value), .BOOL_VEC4 => |*value| c.glGetnUniformuiv(program, location, @sizeOf(@TypeOf(value.*)), value), .FLOAT_MAT2 => |*value| c.glGetUniformfv(program, location, value), .FLOAT_MAT3 => |*value| c.glGetUniformfv(program, location, value), .FLOAT_MAT4 => |*value| c.glGetUniformfv(program, location, value), .FLOAT_MAT2x3 => |*value| c.glGetUniformfv(program, location, value), .FLOAT_MAT2x4 => |*value| c.glGetUniformfv(program, location, value), .FLOAT_MAT3x2 => |*value| c.glGetUniformfv(program, location, value), .FLOAT_MAT3x4 => |*value| c.glGetUniformfv(program, location, value), .FLOAT_MAT4x2 => |*value| c.glGetUniformfv(program, location, value), .FLOAT_MAT4x3 => |*value| c.glGetUniformfv(program, location, value), .DOUBLE_MAT2 => |*value| c.glGetUniformdv(program, location, value), .DOUBLE_MAT3 => |*value| c.glGetUniformdv(program, location, value), .DOUBLE_MAT4 => |*value| c.glGetUniformdv(program, location, value), .DOUBLE_MAT2x3 => |*value| c.glGetUniformdv(program, location, value), .DOUBLE_MAT2x4 => |*value| c.glGetUniformdv(program, location, value), .DOUBLE_MAT3x2 => |*value| c.glGetUniformdv(program, location, value), .DOUBLE_MAT3x4 => |*value| c.glGetUniformdv(program, location, value), .DOUBLE_MAT4x2 => |*value| c.glGetUniformdv(program, location, value), .DOUBLE_MAT4x3 => |*value| c.glGetUniformdv(program, location, value), .SAMPLER_1D, .SAMPLER_2D, .SAMPLER_3D, .SAMPLER_CUBE, .SAMPLER_1D_SHADOW, .SAMPLER_2D_SHADOW, .SAMPLER_1D_ARRAY, .SAMPLER_2D_ARRAY, .SAMPLER_1D_ARRAY_SHADOW, .SAMPLER_2D_ARRAY_SHADOW, .SAMPLER_2D_MULTISAMPLE, .SAMPLER_2D_MULTISAMPLE_ARRAY, .SAMPLER_CUBE_SHADOW, .SAMPLER_BUFFER, .SAMPLER_2D_RECT, .SAMPLER_2D_RECT_SHADOW, .INT_SAMPLER_1D, .INT_SAMPLER_2D, .INT_SAMPLER_3D, .INT_SAMPLER_CUBE, .INT_SAMPLER_1D_ARRAY, .INT_SAMPLER_2D_ARRAY, .INT_SAMPLER_2D_MULTISAMPLE, .INT_SAMPLER_2D_MULTISAMPLE_ARRAY, .INT_SAMPLER_BUFFER, .INT_SAMPLER_2D_RECT, .UNSIGNED_INT_SAMPLER_1D, .UNSIGNED_INT_SAMPLER_2D, .UNSIGNED_INT_SAMPLER_3D, .UNSIGNED_INT_SAMPLER_CUBE, .UNSIGNED_INT_SAMPLER_1D_ARRAY, .UNSIGNED_INT_SAMPLER_2D_ARRAY, .UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE, .UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY, .UNSIGNED_INT_SAMPLER_BUFFER, .UNSIGNED_INT_SAMPLER_2D_RECT => |*value| c.glGetnUniformiv(program, location, @sizeOf(@TypeOf(value.*)), value), } } // zig fmt: on }; pub const ShaderProgram = struct { program_id: c.GLuint, vert_id: c.GLuint, frag_id: c.GLuint, pub fn bind(self: ShaderProgram) void { c.glUseProgram(self.program_id); } pub fn uniformLocation(self: ShaderProgram, name: [*]const u8) !c.GLint { const id = c.glGetUniformLocation(self.program_id, name); if (id == -1) return error.invalidUniform; return id; } pub fn create(vert_source: []const u8, frag_source: []const u8) !ShaderProgram { var self: ShaderProgram = undefined; self.vert_id = try initGlShader(vert_source, "vertex", c.GL_VERTEX_SHADER); errdefer c.glDeleteShader(self.vert_id); self.frag_id = try initGlShader(frag_source, "fragment", c.GL_FRAGMENT_SHADER); errdefer c.glDeleteShader(self.frag_id); self.program_id = c.glCreateProgram(); c.glAttachShader(self.program_id, self.vert_id); c.glAttachShader(self.program_id, self.frag_id); c.glLinkProgram(self.program_id); errdefer c.glDeleteProgram(self.program_id); errdefer c.glDetachShader(self.program_id, self.frag_id); errdefer c.glDetachShader(self.program_id, self.vert_id); var ok: c.GLint = undefined; c.glGetProgramiv(self.program_id, c.GL_LINK_STATUS, &ok); if (ok != 0) return self; var error_size: c.GLint = undefined; c.glGetProgramiv(self.program_id, c.GL_INFO_LOG_LENGTH, &error_size); const message = try c_allocator.alloc(u8, @intCast(usize, error_size)); c.glGetProgramInfoLog(self.program_id, error_size, &error_size, message.ptr); std.debug.warn("Error linking shader program: {s}\n", .{@ptrCast([*:0]const u8, message.ptr)}); return error.LinkError; } pub fn destroy(self: *ShaderProgram) void { c.glDetachShader(self.program_id, self.frag_id); c.glDetachShader(self.program_id, self.vert_id); c.glDeleteShader(self.frag_id); c.glDeleteShader(self.vert_id); c.glDeleteProgram(self.program_id); } }; pub const UniformCache = struct { allocator: *std.mem.Allocator, uniforms: std.StringHashMap(CachedUniform), shader: *ShaderProgram, pub fn init(allocator: *std.mem.Allocator, shader: *ShaderProgram) UniformCache { return UniformCache{ .allocator = allocator, .uniforms = std.StringHashMap(CachedUniform).init(allocator), .shader = shader, }; } pub fn deinit(self: *UniformCache) void { var it = self.uniforms.iterator(); while (it.next()) |entry| { self.allocator.free(entry.key); } self.uniforms.deinit(); } pub fn refresh(self: *UniformCache) !void { var uniform_count: c.GLint = undefined; c.glGetProgramiv(self.shader.program_id, c.GL_ACTIVE_UNIFORMS, &uniform_count); const count = self.uniforms.count(); var existing_names = try c_allocator.alloc([]const u8, count); defer c_allocator.free(existing_names); var existing_names_c = try c_allocator.alloc([*]const u8, count); defer c_allocator.free(existing_names_c); var existing_indices = try c_allocator.alloc(c.GLuint, count); defer c_allocator.free(existing_indices); { var it = self.uniforms.iterator(); var i: usize = 0; while (it.next()) |entry| { existing_names[i] = entry.key; existing_names_c[i] = entry.key.ptr; i += 1; } } c.glGetUniformIndices( self.shader.program_id, @intCast(c.GLsizei, count), existing_names_c.ptr, existing_indices.ptr, ); for (existing_indices) |index, i| { const name = existing_names[i]; if (index == c.GL_INVALID_INDEX) { if (self.uniforms.remove(name)) |removed| { self.allocator.free(removed.key); } } else { var uniform = self.uniforms.get(name).?; uniform.location = self.shader.uniformLocation(name.ptr) catch unreachable; uniform.setShaderValue(self.shader.*); } } } // name has to be zero-terminated! pub fn get(self: *UniformCache, name: []const u8) !?*CachedUniform { const cloned_name = try std.mem.dupe(self.allocator, u8, name); errdefer self.allocator.free(cloned_name); var result = try self.uniforms.getOrPut(cloned_name); if (!result.found_existing) { result.entry.value = CachedUniform.init(self.shader.*, name.ptr) catch { _ = self.uniforms.remove(cloned_name); self.allocator.free(cloned_name); return null; }; } return &result.entry.value; } }; fn initGlShader(source: []const u8, name: []const u8, kind: c.GLenum) !c.GLuint { const shader_id = c.glCreateShader(kind); errdefer c.glDeleteShader(shader_id); const source_ptr: ?[*]const u8 = source.ptr; const source_len = @intCast(c.GLint, source.len); c.glShaderSource(shader_id, 1, &source_ptr, &source_len); c.glCompileShader(shader_id); var ok: c.GLint = undefined; c.glGetShaderiv(shader_id, c.GL_COMPILE_STATUS, &ok); if (ok != 0) return shader_id; var error_size: c.GLint = undefined; c.glGetShaderiv(shader_id, c.GL_INFO_LOG_LENGTH, &error_size); const message = try c_allocator.alloc(u8, @intCast(usize, error_size)); c.glGetShaderInfoLog(shader_id, error_size, &error_size, message.ptr); std.debug.warn( "Error compiling {s} shader:\n{s}\n", .{ name, @ptrCast([*:0]const u8, message.ptr) }, ); return error.CompileError; } pub const FramebufferObject = struct { buffer_id: c.GLuint, texture_id: c.GLuint, pub fn bind(self: FramebufferObject) void { c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.buffer_id); } pub fn unbind(self: FramebufferObject) void { c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0); } pub fn create(width: i32, height: i32) !FramebufferObject { var self: FramebufferObject = undefined; c.glGenFramebuffers(1, &self.buffer_id); c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.buffer_id); c.glGenTextures(1, &self.texture_id); c.glBindTexture(c.GL_TEXTURE_2D, self.texture_id); c.glTexImage2D( c.GL_TEXTURE_2D, 0, c.GL_RGBA, width, height, 0, c.GL_RGBA, c.GL_UNSIGNED_BYTE, null, ); c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MIN_FILTER, c.GL_LINEAR); c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MAG_FILTER, c.GL_LINEAR); c.glBindTexture(c.GL_TEXTURE_2D, 0); c.glFramebufferTexture2D( c.GL_FRAMEBUFFER, c.GL_COLOR_ATTACHMENT0, c.GL_TEXTURE_2D, self.texture_id, 0, ); if (c.glCheckFramebufferStatus(c.GL_FRAMEBUFFER) != c.GL_FRAMEBUFFER_COMPLETE) return error.FBONotComplete; c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0); return self; } pub fn destroy(self: *FramebufferObject) void { c.glDeleteFramebuffers(1, &self.*.buffer_id); c.glDeleteTextures(1, &self.*.texture_id); } }; pub const VertexObject = struct { array_id: c.GLuint, buffer_id: c.GLuint, vertex_size: i32, vertex_count: i32, pub fn bind(self: VertexObject, attrib: u32) void { c.glBindBuffer(c.GL_ARRAY_BUFFER, self.buffer_id); c.glEnableVertexAttribArray(attrib); c.glVertexAttribPointer(attrib, self.vertex_size, c.GL_FLOAT, c.GL_FALSE, 0, null); } pub fn draw(self: VertexObject) void { c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, self.vertex_count); } pub fn create(comptime T: type, vertices: []const T, vertex_size: i32) VertexObject { var self: VertexObject = undefined; self.vertex_size = vertex_size; self.vertex_count = @intCast(i32, vertices.len); c.glGenVertexArrays(1, &self.array_id); c.glBindVertexArray(self.array_id); c.glGenBuffers(1, &self.buffer_id); c.glBindBuffer(c.GL_ARRAY_BUFFER, self.buffer_id); c.glBufferData(c.GL_ARRAY_BUFFER, @intCast(c_long, @sizeOf(T) * vertices.len), @ptrCast(*const c_void, vertices.ptr), c.GL_STATIC_DRAW); return self; } pub fn destroy(self: *VertexObject) void { defer c.glDeleteBuffers(1, &self.*.buffer_id); defer c.glDeleteVertexArrays(1, &self.*.array_id); } }; pub const Constants = struct { texture_shader: ShaderProgram, normalized_quad: VertexObject, main_window: *c.GLFWwindow, aspect: f32, config: *cfg.Config, pub fn create(main_window: *c.GLFWwindow, config: *cfg.Config) !Constants { c.glfwMakeContextCurrent(main_window); const shader = try ShaderProgram.create( \\#version 330 core \\ \\layout(location = 0) in vec2 position; \\out vec2 uv; \\ \\void main() { \\ uv = position / 2.0 + 0.5; \\ gl_Position = vec4(position, 0, 1); \\} , \\#version 330 core \\ \\in vec2 uv; \\out vec4 color; \\ \\uniform sampler2D colorTexture; \\ \\void main(){ \\ color = texture(colorTexture, uv); \\} ); const vertices = [_]c.GLfloat{ -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, }; return Constants{ .main_window = main_window, .texture_shader = shader, .normalized_quad = VertexObject.create(f32, vertices[0..], 2), .config = config, .aspect = @intToFloat(f32, config.width) / @intToFloat(f32, config.height), }; } pub fn destroy(self: *Constants) void { self.normalized_quad.destroy(); self.texture_shader.destroy(); } };