const std = @import("std"); const c_allocator = @import("std").heap.c_allocator; const c = @import("c.zig"); const cfg = @import("config.zig"); fn tmpZ(str: []const u8) ![:0]const u8 { const state = struct { var buf: [256]u8 = undefined; }; return try std.fmt.bufPrintZ(state.buf[0..], "{s}", .{str}); } var image_unit_map = [_]bool{false} ** 1024; pub const Texture = struct { pub const Type = enum(c.GLenum) { TEXTURE_1D = c.GL_TEXTURE_1D, TEXTURE_2D = c.GL_TEXTURE_2D, TEXTURE_3D = c.GL_TEXTURE_3D, TEXTURE_CUBEMAP = c.GL_TEXTURE_CUBE_MAP, TEXTURE_RECTANGLE = c.GL_TEXTURE_RECTANGLE, TEXTURE_2D_MULTISAMPLE = c.GL_TEXTURE_2D_MULTISAMPLE, TEXTURE_1D_ARRAY = c.GL_TEXTURE_1D_ARRAY, TEXTURE_2D_ARRAY = c.GL_TEXTURE_2D_ARRAY, TEXTURE_CUBEMAP_ARRAY = c.GL_TEXTURE_CUBE_MAP_ARRAY, TEXTURE_BUFFER = c.GL_TEXTURE_BUFFER, TEXTURE_2D_MULTISAMPLE_ARRAY = c.GL_TEXTURE_2D_MULTISAMPLE_ARRAY, }; type: Type, id: c.GLuint, image_unit: ?usize, pub fn create(texture_type: Type) Texture { var self: Texture = .{ .type = texture_type, .id = undefined, .image_unit = null, }; c.glGenTextures(1, &self.id); c.glBindTexture(@intFromEnum(self.type), self.id); defer c.glBindTexture(@intFromEnum(self.type), 0); c.glTexParameteri(@intFromEnum(self.type), c.GL_TEXTURE_MIN_FILTER, c.GL_LINEAR); c.glTexParameteri(@intFromEnum(self.type), c.GL_TEXTURE_MAG_FILTER, c.GL_LINEAR); return self; } pub fn bind(self: *Texture) usize { if (self.image_unit == null) { for (image_unit_map[0..], 0..) |*slot, i| { if (slot.*) continue; slot.* = true; self.image_unit = @intCast(i); break; } } if (self.image_unit) |image_unit| { c.glActiveTexture(@intCast(c.GL_TEXTURE0 + image_unit)); c.glBindTexture(@intFromEnum(self.type), self.id); return image_unit; } else unreachable; } pub fn destroy(self: *const Texture) void { if (self.image_unit) |image_unit| { image_unit_map[image_unit] = false; } c.glDeleteTextures(1, &self.id); } pub fn setData2D(self: *const Texture, width: i32, height: i32, data: [*]const u8) void { c.glBindTexture(@intFromEnum(self.type), self.id); defer c.glBindTexture(@intFromEnum(self.type), 0); c.glTexImage2D( @intFromEnum(self.type), 0, c.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, // c.GL_RGBA, width, height, 0, c.GL_RGBA, c.GL_UNSIGNED_BYTE, data, ); } pub fn allocate3D(self: *const Texture, width: i32, height: i32, depth: i32) void { c.glBindTexture(@intFromEnum(self.type), self.id); defer c.glBindTexture(@intFromEnum(self.type), 0); c.glTexStorage3D( @intFromEnum(self.type), 1, c.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, // c.GL_RGBA, width, height, depth, ); } pub fn setLayer3D(self: *const Texture, width: i32, height: i32, layer: i32, data: [*]const u8) void { c.glBindTexture(@intFromEnum(self.type), self.id); defer c.glBindTexture(@intFromEnum(self.type), 0); c.glTexSubImage3D( @intFromEnum(self.type), 0, 0, 0, layer, width, height, 1, c.GL_RGBA, c.GL_UNSIGNED_BYTE, data, ); } }; 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: ?Texture, SAMPLER_2D: ?Texture, SAMPLER_3D: ?Texture, SAMPLER_CUBE: ?Texture, SAMPLER_1D_SHADOW: ?Texture, SAMPLER_2D_SHADOW: ?Texture, SAMPLER_1D_ARRAY: ?Texture, SAMPLER_2D_ARRAY: ?Texture, SAMPLER_1D_ARRAY_SHADOW: ?Texture, SAMPLER_2D_ARRAY_SHADOW: ?Texture, SAMPLER_2D_MULTISAMPLE: ?Texture, SAMPLER_2D_MULTISAMPLE_ARRAY: ?Texture, SAMPLER_CUBE_SHADOW: ?Texture, SAMPLER_BUFFER: ?Texture, SAMPLER_2D_RECT: ?Texture, SAMPLER_2D_RECT_SHADOW: ?Texture, INT_SAMPLER_1D: ?Texture, INT_SAMPLER_2D: ?Texture, INT_SAMPLER_3D: ?Texture, INT_SAMPLER_CUBE: ?Texture, INT_SAMPLER_1D_ARRAY: ?Texture, INT_SAMPLER_2D_ARRAY: ?Texture, INT_SAMPLER_2D_MULTISAMPLE: ?Texture, INT_SAMPLER_2D_MULTISAMPLE_ARRAY: ?Texture, INT_SAMPLER_BUFFER: ?Texture, INT_SAMPLER_2D_RECT: ?Texture, UNSIGNED_INT_SAMPLER_1D: ?Texture, UNSIGNED_INT_SAMPLER_2D: ?Texture, UNSIGNED_INT_SAMPLER_3D: ?Texture, UNSIGNED_INT_SAMPLER_CUBE: ?Texture, UNSIGNED_INT_SAMPLER_1D_ARRAY: ?Texture, UNSIGNED_INT_SAMPLER_2D_ARRAY: ?Texture, UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: ?Texture, UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: ?Texture, UNSIGNED_INT_SAMPLER_BUFFER: ?Texture, UNSIGNED_INT_SAMPLER_2D_RECT: ?Texture, pub fn deinit(self: *const UniformValue) void { switch (self.*) { .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| { if (value) |texture| texture.destroy(); }, else => {}, } } pub fn getPointer(self: *UniformValue) UniformPointer { return switch (self.*) { inline else => |*value, tag| @unionInit(UniformPointer, @tagName(tag), value), }; } }; const UniformPointer = 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: *?Texture, SAMPLER_2D: *?Texture, SAMPLER_3D: *?Texture, SAMPLER_CUBE: *?Texture, SAMPLER_1D_SHADOW: *?Texture, SAMPLER_2D_SHADOW: *?Texture, SAMPLER_1D_ARRAY: *?Texture, SAMPLER_2D_ARRAY: *?Texture, SAMPLER_1D_ARRAY_SHADOW: *?Texture, SAMPLER_2D_ARRAY_SHADOW: *?Texture, SAMPLER_2D_MULTISAMPLE: *?Texture, SAMPLER_2D_MULTISAMPLE_ARRAY: *?Texture, SAMPLER_CUBE_SHADOW: *?Texture, SAMPLER_BUFFER: *?Texture, SAMPLER_2D_RECT: *?Texture, SAMPLER_2D_RECT_SHADOW: *?Texture, INT_SAMPLER_1D: *?Texture, INT_SAMPLER_2D: *?Texture, INT_SAMPLER_3D: *?Texture, INT_SAMPLER_CUBE: *?Texture, INT_SAMPLER_1D_ARRAY: *?Texture, INT_SAMPLER_2D_ARRAY: *?Texture, INT_SAMPLER_2D_MULTISAMPLE: *?Texture, INT_SAMPLER_2D_MULTISAMPLE_ARRAY: *?Texture, INT_SAMPLER_BUFFER: *?Texture, INT_SAMPLER_2D_RECT: *?Texture, UNSIGNED_INT_SAMPLER_1D: *?Texture, UNSIGNED_INT_SAMPLER_2D: *?Texture, UNSIGNED_INT_SAMPLER_3D: *?Texture, UNSIGNED_INT_SAMPLER_CUBE: *?Texture, UNSIGNED_INT_SAMPLER_1D_ARRAY: *?Texture, UNSIGNED_INT_SAMPLER_2D_ARRAY: *?Texture, UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: *?Texture, UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: *?Texture, UNSIGNED_INT_SAMPLER_BUFFER: *?Texture, UNSIGNED_INT_SAMPLER_2D_RECT: *?Texture, pub fn index(self: *const UniformPointer, i: u8) !UniformPointer { const length: u8 = switch (self.*) { .FLOAT_VEC2, .DOUBLE_VEC2, .INT_VEC2, .BOOL_VEC2, .UNSIGNED_INT_VEC2 => 2, .FLOAT_VEC3, .DOUBLE_VEC3, .INT_VEC3, .UNSIGNED_INT_VEC3 => 3, .BOOL_VEC3, .FLOAT_VEC4, .DOUBLE_VEC4, .INT_VEC4, .UNSIGNED_INT_VEC4, .BOOL_VEC4 => 4, else => return error.notIndexable, }; if (i >= length) { return error.invalidIndex; } return switch (self.*) { inline .FLOAT_VEC2, .FLOAT_VEC3, .FLOAT_VEC4 => |v| .{ .FLOAT = &v[i] }, inline .DOUBLE_VEC2, .DOUBLE_VEC3, .DOUBLE_VEC4 => |v| .{ .DOUBLE = &v[i] }, inline .INT_VEC2, .INT_VEC3, .INT_VEC4 => |v| .{ .INT = &v[i] }, inline .UNSIGNED_INT_VEC2, .UNSIGNED_INT_VEC3, .UNSIGNED_INT_VEC4 => |v| .{ .UNSIGNED_INT = &v[i] }, inline .BOOL_VEC2, .BOOL_VEC3, .BOOL_VEC4 => |v| .{ .BOOL = &v[i] }, else => unreachable, }; } }; pub const CachedUniform = struct { value: ?*UniformValue, location: c.GLint, pub fn init(allocator: std.mem.Allocator, shader: ShaderProgram, name: []const u8) !CachedUniform { var index: c.GLuint = undefined; const nameZ = try tmpZ(name); c.glGetUniformIndices(shader.program_id, 1, &nameZ.ptr, &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); switch (@as(UniformType, @enumFromInt(_utype))) { inline else => |utype| { const location = try shader.uniformLocation(nameZ); const value = try allocator.create(UniformValue); errdefer allocator.destroy(value); value.* = @unionInit(UniformValue, @tagName(utype), undefined); return .{ .value = value, .location = location }; }, } } pub fn deinit(self: *CachedUniform, allocator: std.mem.Allocator) void { if (self.value) |value| { value.deinit(); allocator.destroy(value); self.value = null; } } // deinit self WITHOUT freeing contained resources pub fn move(self: *CachedUniform, to: *CachedUniform, allocator: std.mem.Allocator) CachedUniform { to.deinit(allocator); to.buffer = self.buffer; to.value = self.value; self.buffer = null; return self; } pub fn tryMove(self: *CachedUniform, dest: *CachedUniform, allocator: std.mem.Allocator) bool { if (@as(UniformType, self.value.?.*) != dest.value.?.*) return false; dest.deinit(allocator); dest.value = self.value; self.value = null; return true; } 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| { if (value.*) |*texture| { const index = texture.bind(); c.glProgramUniform1i(program, location, @intCast(index)); } }, } } 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| value.* = null, } } }; 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, nameZ: [:0]const u8) !c.GLint { const id = c.glGetUniformLocation(self.program_id, nameZ); 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, @as(usize, @intCast(error_size))); c.glGetProgramInfoLog(self.program_id, error_size, &error_size, message.ptr); std.debug.print("Error linking shader program: {s}\n", .{@as([*:0]const u8, @ptrCast(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| { entry.value_ptr.deinit(self.allocator); self.allocator.free(entry.key_ptr.*); } self.uniforms.deinit(); } pub fn get(self: *UniformCache, name: []const u8) !?*CachedUniform { if (!self.uniforms.contains(name)) { const cloned_name = try self.allocator.dupe(u8, name); errdefer self.allocator.free(cloned_name); var uniform = CachedUniform.init(self.allocator, self.shader.*, name) catch return null; errdefer uniform.deinit(self.allocator); uniform.getShaderValue(self.shader.*); try self.uniforms.put(cloned_name, uniform); } if (self.uniforms.getPtr(name)) |ptr| { return ptr; } return null; } pub fn refresh(self: *UniformCache) !void { var old_uniforms = self.uniforms.move(); defer { var it = old_uniforms.iterator(); while (it.next()) |entry| { self.allocator.free(entry.key_ptr.*); entry.value_ptr.deinit(self.allocator); } old_uniforms.deinit(); } { var it = old_uniforms.iterator(); while (it.next()) |old_entry| { if (try self.get(old_entry.key_ptr.*)) |uniform| { if (old_entry.value_ptr.tryMove(uniform, self.allocator)) { uniform.setShaderValue(self.shader.*); } } } } } }; 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 = @as(c.GLint, @intCast(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, @as(usize, @intCast(error_size))); c.glGetShaderInfoLog(shader_id, error_size, &error_size, message.ptr); std.debug.print( "Error compiling {s} shader:\n{s}\n", .{ name, @as([*:0]const u8, @ptrCast(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 { _ = self; 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); defer c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0); c.glGenTextures(1, &self.texture_id); c.glBindTexture(c.GL_TEXTURE_2D, self.texture_id); defer c.glBindTexture(c.GL_TEXTURE_2D, 0); 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.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; 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 = @as(i32, @intCast(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, @as(c_long, @intCast(@sizeOf(T) * vertices.len)), @as(*const anyopaque, @ptrCast(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 = @as(f32, @floatFromInt(config.width)) / @as(f32, @floatFromInt(config.height)), }; } pub fn destroy(self: *Constants) void { self.normalized_quad.destroy(); self.texture_shader.destroy(); } };