You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

590 lines
27 KiB
Zig

const std = @import("std");
const c_allocator = @import("std").heap.c_allocator;
const panic = @import("std").debug.panic;
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: [*:0]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 attribLocation(self: ShaderProgram, name: [*:0]const u8) c.GLint {
const id = c.glGetAttribLocation(self.program_id, name);
if (id == -1) {
panic("invalid attrib: {}\n", .{name});
}
return id;
}
// TODO: we can ensure to have 0-terminated names via [*:0]const u8 ?
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.print("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_ptr.*);
}
self.uniforms.deinit();
}
pub fn refresh(self: *UniformCache) !void {
// FIXME: this fails, when uniforms are reordered in the shader,
// as cached indices map to the wrong uniforms then
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_ptr.*;
existing_names_c[i] = entry.key_ptr.*.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.fetchRemove(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.*);
}
}
}
pub fn get(self: *UniformCache, name: [:0]const u8) !?*CachedUniform {
const cloned_name = try self.allocator.dupe(u8, name);
errdefer self.allocator.free(cloned_name);
var result = try self.uniforms.getOrPut(cloned_name);
if (!result.found_existing) {
result.value_ptr.* = CachedUniform.init(self.shader.*, name.ptr) catch {
_ = self.uniforms.remove(cloned_name);
self.allocator.free(cloned_name);
return null;
};
}
return result.value_ptr;
}
};
// TODO: enforce param 0-termination?
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.print("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, self.buffer_id);
}
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 anyopaque, 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);
}
};
// Constants encapsulates static values needed for rendering to Outputs.
pub const Constants = struct {
// outputs bind this, it draws the texture containing the rendered user shader
texture_shader: ShaderProgram,
normalized_quad: VertexObject,
main_window: *c.GLFWwindow, // shared main window context
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();
}
};