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});
}
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(allocator: std.mem.Allocator, comptime utype: UniformType) !UniformValue {
const data = try allocator.create(std.meta.Child(std.meta.TagPayload(UniformValue, utype)));
return @unionInit(UniformValue, @tagName(utype), data);
}
pub fn deinit(self: *const UniformValue, allocator: std.mem.Allocator) void {
switch (self.*) {
inline else => |value| allocator.destroy(value),
}
}
pub fn index(self: *const UniformValue, i: u8) !UniformValue {
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.*) {
.FLOAT_VEC2, .FLOAT_VEC3, .FLOAT_VEC4 => |v| .{ .FLOAT = &v[i] },
.DOUBLE_VEC2, .DOUBLE_VEC3, .DOUBLE_VEC4 => |v| .{ .DOUBLE = &v[i] },
.INT_VEC2, .INT_VEC3, .INT_VEC4 => |v| .{ .INT = &v[i] },
.UNSIGNED_INT_VEC2, .UNSIGNED_INT_VEC3, .UNSIGNED_INT_VEC4 => |v| .{ .UNSIGNED_INT = &v[i] },
.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 UniformValue.init(allocator, utype);
errdefer value.deinit(allocator);
return .{ .value = value, .location = location };
},
}
}
pub fn deinit(self: *const CachedUniform, allocator: std.mem.Allocator) void {
self.value.deinit(allocator);
}
pub fn tryCopy(self: *CachedUniform, from: CachedUniform) bool {
if (@as(UniformType, self.value) != from.value) return false;
switch (self.value) {
inline else => |*val, tag| {
val.*.* = @field(from.value, @tagName(tag)).*;
},
}
return true;
}
// 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, 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 {
var getValue = false;
if (!self.uniforms.contains(name)) {
const cloned_name = try self.allocator.dupe(u8, name);
errdefer self.allocator.free(cloned_name);
const uniform = CachedUniform.init(self.allocator, self.shader.*, name) catch return null;
errdefer uniform.deinit(self.allocator);
try self.uniforms.put(cloned_name, uniform);
getValue = true;
}
if (self.uniforms.getPtr(name)) |ptr| {
ptr.getShaderValue(self.shader.*);
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 (uniform.tryCopy(old_entry.value_ptr.*)) {
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);
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 = @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();
}
};