diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2020-01-23 14:00:16 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2020-01-23 15:06:29 +0000 |
| commit | 866fccb01e40ab11add47511c922ec2dd8f0454e (patch) | |
| tree | 47cde0299aa8225860fc2bfee52b8abde329fad5 | |
| parent | initial commit (diff) | |
| download | glsl-view-866fccb01e40ab11add47511c922ec2dd8f0454e.tar.gz glsl-view-866fccb01e40ab11add47511c922ec2dd8f0454e.zip | |
Code reorganisation
| -rw-r--r-- | src/gl.zig | 255 | ||||
| -rw-r--r-- | src/main.zig | 92 | ||||
| -rw-r--r-- | src/output.zig | 31 | ||||
| -rw-r--r-- | src/shader.zig | 118 |
4 files changed, 347 insertions, 149 deletions
diff --git a/src/gl.zig b/src/gl.zig new file mode 100644 index 0000000..c974cea --- /dev/null +++ b/src/gl.zig @@ -0,0 +1,255 @@ +const c_allocator = @import("std").heap.c_allocator; +const panic = @import("std").debug.panic; +const c = @import("c.zig"); + +pub const ShaderProgram = struct { + program_id: c.GLuint, + vertex_id: c.GLuint, + fragment_id: c.GLuint, + maybe_geometry_id: ?c.GLuint, + + pub fn bind(self: ShaderProgram) void { + c.glUseProgram(self.program_id); + } + + pub fn attribLocation(self: ShaderProgram, name: [*]const u8) c.GLint { + const id = c.glGetAttribLocation(self.program_id, name); + if (id == -1) { + panic("invalid attrib: {}\n", .{name}); + } + return id; + } + + pub fn uniformLocation(self: ShaderProgram, name: [*]const u8) c.GLint { + const id = c.glGetUniformLocation(self.program_id, name); + if (id == -1) { + panic("invalid uniform: {}\n", .{name}); + } + return id; + } + + pub fn setUniformInt(self: ShaderProgram, uniform_id: c.GLint, value: c_int) void { + c.glUniform1i(uniform_id, value); + } + + pub fn setUniformFloat(self: ShaderProgram, uniform_id: c.GLint, value: f32) void { + c.glUniform1f(uniform_id, value); + } + + pub fn setUniformVec2(self: ShaderProgram, uniform_id: c.GLint, value: [2]f32) void { + c.glUniform3fv(uniform_id, 1, value.ptr); + } + + pub fn setUniformVec3(self: ShaderProgram, uniform_id: c.GLint, value: [3]f32) void { + c.glUniform3fv(uniform_id, 1, value.ptr); + } + + pub fn setUniformVec4(self: ShaderProgram, uniform_id: c.GLint, value: [4]f32) void { + c.glUniform4fv(uniform_id, 1, value.ptr); + } + + pub fn setUniformMat4x4(self: ShaderProgram, uniform_id: c.GLint, value: [4][4]f32) void { + c.glUniformMatrix4fv(uniform_id, 1, c.GL_FALSE, &value[0][0]); + } + + pub fn create( + vertex_source: []const u8, + frag_source: []const u8, + maybe_geometry_source: ?[]u8, + ) !ShaderProgram { + var self: ShaderProgram = undefined; + self.vertex_id = try initGlShader(vertex_source, "vertex", c.GL_VERTEX_SHADER); + self.fragment_id = try initGlShader(frag_source, "fragment", c.GL_FRAGMENT_SHADER); + self.maybe_geometry_id = if (maybe_geometry_source) |geo_source| + try initGlShader(geo_source, "geometry", c.GL_GEOMETRY_SHADER) + else + null; + + self.program_id = c.glCreateProgram(); + c.glAttachShader(self.program_id, self.vertex_id); + c.glAttachShader(self.program_id, self.fragment_id); + if (self.maybe_geometry_id) |geo_id| { + c.glAttachShader(self.program_id, geo_id); + } + c.glLinkProgram(self.program_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); + panic("Error linking shader program: {s}\n", .{ @ptrCast([*:0]const u8, message.ptr) }); + } + + pub fn destroy(self: *ShaderProgram) void { + if (self.maybe_geometry_id) |geo_id| { + c.glDetachShader(self.program_id, geo_id); + } + c.glDetachShader(self.program_id, self.fragment_id); + c.glDetachShader(self.program_id, self.vertex_id); + + if (self.maybe_geometry_id) |geo_id| { + c.glDeleteShader(geo_id); + } + c.glDeleteShader(self.fragment_id); + c.glDeleteShader(self.vertex_id); + + c.glDeleteProgram(self.program_id); + } +}; + +fn initGlShader(source: []const u8, name: []const u8, kind: c.GLenum) !c.GLuint { + const shader_id = c.glCreateShader(kind); + 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); + panic("Error compiling {s} shader:\n{s}\n", .{ name, @ptrCast([*:0]const u8, message.ptr) }); +} + +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[0]), + 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 { + textureShader: ShaderProgram, + normalizedQuad: VertexObject, + main_window: *c.GLFWwindow, + + pub fn create(main_window: *c.GLFWwindow) !Constants { + var self: Constants = undefined; + + c.glfwMakeContextCurrent(main_window); + self.main_window = main_window; + + self.textureShader = 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); + \\} + , + null + ); + + const vertices = [_]c.GLfloat{ + -1.0, -1.0, + -1.0, 1.0, + 1.0, -1.0, + 1.0, 1.0, + }; + self.normalizedQuad = VertexObject.create(f32, vertices[0..], 2); + + return self; + } + + pub fn destroy(self: *Constants) void { + self.normalizedQuad.destroy(); + self.textureShader.destroy(); + } +}; diff --git a/src/main.zig b/src/main.zig index ce31e75..6d119af 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4,6 +4,7 @@ const c = @import("c.zig"); const debug_gl = @import("debug_gl.zig"); const cfg = @import("config.zig"); const out = @import("output.zig"); +const gl = @import("gl.zig"); var window: *c.GLFWwindow = undefined; @@ -22,60 +23,107 @@ pub fn main() !void { } defer c.glfwTerminate(); - c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MAJOR, 3); + c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MAJOR, 4); c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MINOR, 2); c.glfwWindowHint(c.GLFW_OPENGL_FORWARD_COMPAT, c.GL_TRUE); c.glfwWindowHint(c.GLFW_OPENGL_DEBUG_CONTEXT, debug_gl.is_on); c.glfwWindowHint(c.GLFW_OPENGL_PROFILE, c.GLFW_OPENGL_CORE_PROFILE); c.glfwWindowHint(c.GLFW_DEPTH_BITS, 0); - c.glfwWindowHint(c.GLFW_STENCIL_BITS, 8); + c.glfwWindowHint(c.GLFW_STENCIL_BITS, 0); c.glfwWindowHint(c.GLFW_VISIBLE, c.GLFW_FALSE); window = c.glfwCreateWindow(config.width, config.height, "glsl-view", null, null) orelse { panic("unable to create window\n", .{}); }; defer c.glfwDestroyWindow(window); - + c.glfwMakeContextCurrent(window); c.glfwSwapInterval(1); - c.glClearColor(0.0, 0.0, 0.0, 1.0); - - // c.glEnable(c.GL_BLEND); - // c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA); - // c.glPixelStorei(c.GL_UNPACK_ALIGNMENT, 1); - - c.glViewport(0, 0, config.width, config.height); - - debug_gl.assertNoError(); - - var outputs = try config.arena.allocator.alloc(*out.Output, config.outputs.len); + var constants = try gl.Constants.create(window); + defer constants.destroy(); + + var outputs = try std.ArrayList(*out.Output).initCapacity(&config.arena.allocator, config.outputs.len); + defer outputs.deinit(); for (config.outputs) |output_config, i| { - outputs[i] = out.Output.create(&config.arena.allocator, output_config, window); + try outputs.append(out.Output.create(&config.arena.allocator, output_config, &constants)); } - defer for (outputs) |output| { + defer for (outputs.toSlice()) |output| { output.destroy(output); }; + c.glfwMakeContextCurrent(window); + c.glClearColor(0.0, 0.0, 0.0, 1.0); + + debug_gl.assertNoError(); + const start_time = c.glfwGetTime(); var prev_time = start_time; + constants.normalizedQuad.bind(0); + + debug_gl.assertNoError(); + + var redShader = try gl.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 float offset; + \\ + \\void main() { + \\ color = vec4(mod(uv + offset, vec2(1)), 0, 1); + \\} + , + null + ); + defer redShader.destroy(); + + var fbo = try gl.FramebufferObject.create(config.width, config.height); + defer fbo.destroy(); + + var offset: f32 = 0; + while (c.glfwWindowShouldClose(window) == c.GL_FALSE) { - c.glClear(c.GL_COLOR_BUFFER_BIT | c.GL_DEPTH_BUFFER_BIT | c.GL_STENCIL_BUFFER_BIT); + c.glfwMakeContextCurrent(window); + c.glClear(c.GL_COLOR_BUFFER_BIT); const now_time = c.glfwGetTime(); const elapsed = now_time - prev_time; prev_time = now_time; + + fbo.bind(); + c.glClear(c.GL_COLOR_BUFFER_BIT); + + redShader.bind(); + offset += @floatCast(f32, elapsed / 4); + redShader.setUniformFloat(redShader.uniformLocation("offset"), offset); + constants.normalizedQuad.draw(); + fbo.unbind(); - for (outputs) |output| { - const close = output.update(output); + for (outputs.toSlice()) |output, i| { + const close = output.update(output, fbo.texture_id); if (close) { - c.glfwSetWindowShouldClose(window, c.GL_TRUE); + const removed = outputs.swapRemove(i); + removed.destroy(removed); + break; } } - c.glfwSwapBuffers(window); - + if (outputs.len == 0) + break; + c.glfwPollEvents(); } diff --git a/src/output.zig b/src/output.zig index 8303107..077387c 100644 --- a/src/output.zig +++ b/src/output.zig @@ -1,21 +1,22 @@ const c = @import("c.zig"); const std = @import("std"); const cfg = @import("config.zig"); +const gl = @import("gl.zig"); pub const Output = struct { - update: fn (self: *Output) bool, - destroy: fn (self: *Output) void, + update: fn (*Output, c.GLuint) bool, + destroy: fn (*Output) void, - pub fn init(update: fn (*Output) bool, destroy: fn (*Output) void) Output { + pub fn init(update: fn (*Output, c.GLuint) bool, destroy: fn (*Output) void) Output { return Output{ .update = update, .destroy = destroy, }; } - pub fn create(allocator: *std.mem.Allocator, config: cfg.OutputConfig, share_window: *c.GLFWwindow) *Output { + pub fn create(allocator: *std.mem.Allocator, config: cfg.OutputConfig, constants: *gl.Constants) *Output { return switch (config.type) { - cfg.OutputType.window => WindowOutput.create(allocator, config, share_window), + cfg.OutputType.window => WindowOutput.create(allocator, config, constants), else => unreachable, }; } @@ -24,15 +25,17 @@ pub const Output = struct { pub const WindowOutput = struct { output: Output, window: *c.GLFWwindow, + constants: *gl.Constants, - pub fn create(allocator: *std.mem.Allocator, config: cfg.OutputConfig, share_window: *c.GLFWwindow) *Output { + pub fn create(allocator: *std.mem.Allocator, config: cfg.OutputConfig, constants: *gl.Constants) *Output { const self = allocator.create(WindowOutput) catch unreachable; c.glfwDefaultWindowHints(); self.* = WindowOutput{ + .constants = constants, .output = Output.init(update, destroy), - .window = c.glfwCreateWindow(config.width, config.height, "glsl-view output", null, share_window) orelse { + .window = c.glfwCreateWindow(config.width, config.height, "glsl-view output", null, constants.main_window) orelse { std.debug.panic("unable to create output window\n", .{}); }, }; @@ -40,17 +43,27 @@ pub const WindowOutput = struct { c.glfwSetWindowUserPointer(self.*.window, @ptrCast(*c_void, self)); _ = c.glfwSetKeyCallback(self.*.window, keyCallback); + c.glfwMakeContextCurrent(self.*.window); + constants.normalizedQuad.bind(0); + return &self.*.output; } - fn update(output: *Output) bool { + fn update(output: *Output, texture_id: c.GLuint) bool { const self = @fieldParentPtr(WindowOutput, "output", output); if (c.glfwWindowShouldClose(self.*.window) == c.GL_TRUE) return true; - c.glClear(c.GL_COLOR_BUFFER_BIT | c.GL_DEPTH_BUFFER_BIT | c.GL_STENCIL_BUFFER_BIT); + c.glfwMakeContextCurrent(self.*.window); + c.glClear(c.GL_COLOR_BUFFER_BIT); + + c.glBindTexture(c.GL_TEXTURE_2D, texture_id); + c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4); + self.constants.textureShader.bind(); + self.constants.normalizedQuad.draw(); + c.glfwSwapBuffers(self.*.window); return false; } diff --git a/src/shader.zig b/src/shader.zig deleted file mode 100644 index ed36035..0000000 --- a/src/shader.zig +++ /dev/null @@ -1,118 +0,0 @@ -const std = @import("std"); -const panic = std.debug.panic -const c = @import("c.zig"); - - -pub const ShaderProgram = struct { - program_id: c.GLuint, - vertex_id: c.GLuint, - fragment_id: c.GLuint, - maybe_geometry_id: ?c.GLuint, - - pub fn bind(sp: ShaderProgram) void { - c.glUseProgram(sp.program_id); - } - - pub fn attribLocation(sp: ShaderProgram, name: [*]const u8) c.GLint { - const id = c.glGetAttribLocation(sp.program_id, name); - if (id == -1) { - panic("invalid attrib: {}\n", .{name}); - } - return id; - } - - pub fn uniformLocation(sp: ShaderProgram, name: [*]const u8) c.GLint { - const id = c.glGetUniformLocation(sp.program_id, name); - if (id == -1) { - panic("invalid uniform: {}\n", .{name}); - } - return id; - } - - pub fn setUniformInt(sp: ShaderProgram, uniform_id: c.GLint, value: c_int) void { - c.glUniform1i(uniform_id, value); - } - - pub fn setUniformFloat(sp: ShaderProgram, uniform_id: c.GLint, value: f32) void { - c.glUniform1f(uniform_id, value); - } - - pub fn setUniformVec3(sp: ShaderProgram, uniform_id: c.GLint, value: math3d.Vec3) void { - c.glUniform3fv(uniform_id, 1, value.data[0..].ptr); - } - - pub fn setUniformVec4(sp: ShaderProgram, uniform_id: c.GLint, value: Vec4) void { - c.glUniform4fv(uniform_id, 1, value.data[0..].ptr); - } - - pub fn setUniformMat4x4(sp: ShaderProgram, uniform_id: c.GLint, value: Mat4x4) void { - c.glUniformMatrix4fv(uniform_id, 1, c.GL_FALSE, value.data[0][0..].ptr); - } - - pub fn create( - vertex_source: []const u8, - frag_source: []const u8, - maybe_geometry_source: ?[]u8, - ) !ShaderProgram { - var sp: ShaderProgram = undefined; - sp.vertex_id = try initGlShader(vertex_source, "vertex", c.GL_VERTEX_SHADER); - sp.fragment_id = try initGlShader(frag_source, "fragment", c.GL_FRAGMENT_SHADER); - sp.maybe_geometry_id = if (maybe_geometry_source) |geo_source| - try initGlShader(geo_source, "geometry", c.GL_GEOMETRY_SHADER) - else - null; - - sp.program_id = c.glCreateProgram(); - c.glAttachShader(sp.program_id, sp.vertex_id); - c.glAttachShader(sp.program_id, sp.fragment_id); - if (sp.maybe_geometry_id) |geo_id| { - c.glAttachShader(sp.program_id, geo_id); - } - c.glLinkProgram(sp.program_id); - - var ok: c.GLint = undefined; - c.glGetProgramiv(sp.program_id, c.GL_LINK_STATUS, &ok); - if (ok != 0) return sp; - - var error_size: c.GLint = undefined; - c.glGetProgramiv(sp.program_id, c.GL_INFO_LOG_LENGTH, &error_size); - const message = try c_allocator.alloc(u8, @intCast(usize, error_size)); - c.glGetProgramInfoLog(sp.program_id, error_size, &error_size, message.ptr); - panic("Error linking shader program: {}\n", .{message.ptr}); - } - - pub fn destroy(sp: *ShaderProgram) void { - if (sp.maybe_geometry_id) |geo_id| { - c.glDetachShader(sp.program_id, geo_id); - } - c.glDetachShader(sp.program_id, sp.fragment_id); - c.glDetachShader(sp.program_id, sp.vertex_id); - - if (sp.maybe_geometry_id) |geo_id| { - c.glDeleteShader(geo_id); - } - c.glDeleteShader(sp.fragment_id); - c.glDeleteShader(sp.vertex_id); - - c.glDeleteProgram(sp.program_id); - } -}; - -fn initGlShader(source: []const u8, name: [*]const u8, kind: c.GLenum) !c.GLuint { - const shader_id = c.glCreateShader(kind); - 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); - panic("Error compiling {} shader:\n{}\n", .{ name, message.ptr }); -} |
