From 9d23bbc39ecb9b5dbae16b8437a347c83bf6bf4f Mon Sep 17 00:00:00 2001 From: Dendy Faist Date: Mon, 2 Jan 2023 02:28:57 +0100 Subject: [PATCH] WIP2: Text/texture rendering --- src/Renderer.zig | 238 +++++++++++----------- src/shaders/color.frag | 7 + src/shaders/{vector.vs => color.vert} | 10 +- src/shaders/{fragment.fs => texture.frag} | 4 +- src/shaders/texture.vert | 15 ++ 5 files changed, 151 insertions(+), 123 deletions(-) create mode 100644 src/shaders/color.frag rename src/shaders/{vector.vs => color.vert} (53%) rename src/shaders/{fragment.fs => texture.frag} (51%) create mode 100644 src/shaders/texture.vert diff --git a/src/Renderer.zig b/src/Renderer.zig index c1b9dae..2e85f70 100644 --- a/src/Renderer.zig +++ b/src/Renderer.zig @@ -8,18 +8,24 @@ const Self = @This(); window: sdl.Window, context: sdl.gl.Context, -//color_loc: u32, buffer: gl.Buffer, -color: [4]f32, +color: [4]f32 = .{ 0, 0, 0, 0 }, -test_counter: usize = 0, +// There's a vbo for each shader program +vbo: [nPrograms][max_objects]f32 = .{.{0.0} ** max_objects} ** nPrograms, +vbo_index: [nPrograms]usize = .{0} ** nPrograms, -vbo: [max_objects]f32 = .{0.0} ** max_objects, -//colors: [max_objects][4]f32 = .{[4]f32{ 0.0, 0.0, 0.0, 1.0 }} ** max_objects, -vbo_index: usize = 0, +// The index of the program is the enum converted to int +programs: [nPrograms]gl.Program = .{undefined} ** nPrograms, const max_objects: usize = 16384; +const shaderProgram = enum { + color, + texture, +}; +const nPrograms = std.meta.fields(shaderProgram).len; + fn glGetProcAddress(p: []const u8, proc: [:0]const u8) ?*const anyopaque { _ = p; return sdl.c.SDL_GL_GetProcAddress(@ptrCast([*c]const u8, proc)); @@ -43,57 +49,30 @@ pub fn init() !Self { .{ .context = .opengl, .vis = .shown }, ); + const proc: []const u8 = undefined; + try gl.loadExtensions(proc, glGetProcAddress); + const ctx = try sdl.gl.createContext(window); sdl.gl.setSwapInterval(.immediate) catch { std.debug.print("WARNING: Unable to configure the swap interval.\n", .{}); }; - const proc: []const u8 = undefined; - try gl.loadExtensions(proc, glGetProcAddress); + gl.enable(.blend); + gl.blendFunc(.src_alpha, .one_minus_src_alpha); - // Shader stuff - var mvp_loc: u32 = undefined; - const program = gl.Program.create(); - { - const vs = gl.Shader.create(.vertex); - defer vs.delete(); - const fs = gl.Shader.create(.fragment); - defer fs.delete(); + var buf = gl.Buffer.gen(); + buf.bind(.array_buffer); - vs.source(1, &.{@embedFile("shaders/vector.vs")}); - fs.source(1, &.{@embedFile("shaders/fragment.fs")}); + var renderer = Self{ .window = window, .context = ctx, .buffer = buf }; - vs.compile(); - fs.compile(); - - program.attach(vs); - defer program.detach(vs); - program.attach(fs); - defer program.detach(fs); - - program.link(); - - mvp_loc = program.uniformLocation("mvp").?; - } - program.use(); - - // Get the matrix that converts coordinates to 1:1 on the screen from the OpenGL default - { - const wsize = window.getSize(); - const xunit = @intToFloat(f32, wsize.width); - const yunit = @intToFloat(f32, wsize.height); - - const ortho = m.Mat4.createOrthogonal(0.0, xunit, yunit, 0.0, 0.0, 100.0); - - gl.uniformMatrix4fv(mvp_loc, false, &.{ortho.fields}); + // Load the shader programs according to the names in the enum + inline for (std.meta.fields(shaderProgram)) |programEnum| { + renderer.programs[programEnum.value] = renderer.loadProgram(programEnum.name); } var vertex_array = gl.VertexArray.gen(); vertex_array.bind(); - const buf = gl.Buffer.gen(); - buf.bind(.array_buffer); - gl.vertexAttribPointer(0, 2, .float, false, @sizeOf(f32) * 8, @sizeOf(f32) * 0); gl.enableVertexAttribArray(0); @@ -105,31 +84,74 @@ pub fn init() !Self { gl.clearColor(0.91, 0.85, 0.65, 1.00); - // TODO: See if there's a more optimal solution to this, maybe activating only when necessary - gl.enable(.blend); - gl.blendFunc(.src_alpha, .one_minus_src_alpha); + var text = Texture.fromText("testing123"); + _ = text; + //text.deinit(); - var text = Text.init("testing123"); - text.deinit(); + return renderer; +} - return Self{ .window = window, .context = ctx, .buffer = buf, .color = .{ 0, 0, 0, 0 } }; +fn loadProgram(self: Self, comptime name: []const u8) gl.Program { + var program = gl.Program.create(); + + const vs = gl.Shader.create(.vertex); + defer vs.delete(); + const fs = gl.Shader.create(.fragment); + defer fs.delete(); + + vs.source(1, &.{@embedFile("shaders/" ++ name ++ ".vert")}); + fs.source(1, &.{@embedFile("shaders/" ++ name ++ ".frag")}); + + vs.compile(); + fs.compile(); + + program.attach(vs); + defer program.detach(vs); + program.attach(fs); + defer program.detach(fs); + + program.link(); + + // Get the matrix that converts coordinates to 1:1 on the screen from the OpenGL default + if (program.uniformLocation("mvp")) |mvpLocation| { + const wsize = self.window.getSize(); + const xunit = @intToFloat(f32, wsize.width); + const yunit = @intToFloat(f32, wsize.height); + + const ortho = m.Mat4.createOrthogonal(0.0, xunit, yunit, 0.0, 0.0, 100.0); + + program.use(); + gl.uniformMatrix4fv(mvpLocation, false, &.{ortho.fields}); + } + + return program; } pub fn render(self: *Self) void { - //std.debug.print("Number of rendered elements: {}\n", .{self.test_counter}); - self.test_counter = 0; + // Render with each of the defined shader programs + for (self.programs) |_, program_i| { + self.programs[program_i].use(); - self.buffer.data(f32, &self.vbo, .static_draw); + self.buffer.data(f32, &(self.vbo[program_i]), .static_draw); + + switch (@intToEnum(shaderProgram, program_i)) { + .color => { + gl.drawArrays(.triangles, 0, self.vbo_index[program_i]); + }, + .texture => { + gl.drawArrays(.triangles, 0, self.vbo_index[program_i]); + }, + } + + self.vbo_index[program_i] = 0; + + // Clear the vbo, this really shouldn't be necessary as the index is set to zero, + // but otherwise it leads to bugs + self.vbo[program_i] = .{0.0} ** max_objects; + } - gl.drawArrays(.triangles, 0, self.vbo_index); sdl.gl.swapWindow(self.window); gl.clear(.{ .color = true }); - - self.vbo_index = 0; - - // Clear the vbo, this really shouldn't be necessary as the index is set to zero, - // but otherwise it leads to bugs - self.vbo = .{0.0} ** max_objects; } pub fn deinit(self: *Self) void { @@ -168,8 +190,7 @@ pub fn fillRectangle(self: *Self, x: i32, y: i32, w: i32, h: i32) void { } pub fn fillRectangleEx(self: *Self, x: i32, y: i32, w: i32, h: i32, skew_x: i32) void { - // TODO: Is this still necessary? - self.test_counter += 1; + const program_i = @enumToInt(shaderProgram.texture); var xf = @intToFloat(f32, x); var yf = @intToFloat(f32, y); @@ -178,29 +199,35 @@ pub fn fillRectangleEx(self: *Self, x: i32, y: i32, w: i32, h: i32, skew_x: i32) const skew_x_offset = @intToFloat(f32, skew_x) * hf / 100; - const i = self.vbo_index; + const i = self.vbo_index[program_i]; const vertex_data = [_]f32{ xf + skew_x_offset, yf, // up-left - self.color[0], self.color[1], self.color[2], self.color[3], - 0.0, 0.0, + self.color[0], self.color[1], + self.color[2], self.color[3], + 0.0, 0.0, xf + wf + skew_x_offset, yf, // up-right - self.color[0], self.color[1], self.color[2], self.color[3], - 1.0, 0.0, + self.color[0], self.color[1], + self.color[2], self.color[3], + 1.0, 0.0, xf - skew_x_offset, yf + hf, // down-left - self.color[0], self.color[1], self.color[2], self.color[3], - 0.0, 1.0, + self.color[0], self.color[1], + self.color[2], self.color[3], + 0.0, 1.0, xf + wf - skew_x_offset, yf + hf, // down-right - self.color[0], self.color[1], self.color[2], self.color[3], - 1.0, 1.0, + self.color[0], self.color[1], + self.color[2], self.color[3], + 1.0, 1.0, xf + wf + skew_x_offset, yf, // up-right - self.color[0], self.color[1], self.color[2], self.color[3], - 1.0, 0.0, + self.color[0], self.color[1], + self.color[2], self.color[3], + 1.0, 0.0, xf - skew_x_offset, yf + hf, // down-left - self.color[0], self.color[1], self.color[2], self.color[3], - 0.0, 1.0, + self.color[0], self.color[1], + self.color[2], self.color[3], + 0.0, 1.0, }; - std.mem.copy(f32, self.vbo[i .. i + vertex_data.len], &vertex_data); - self.vbo_index += vertex_data.len; + std.mem.copy(f32, self.vbo[program_i][i..], &vertex_data); + self.vbo_index[program_i] += vertex_data.len; } fn renderRectangle(self: *Self, x: i32, y: i32, w: i32, h: i32) void { @@ -216,46 +243,14 @@ pub fn getOutputSize(self: Self) OutputSize { }; } -const image = [_]u8{ - // 1 - 0,0,0, - 255,255,255, - 0,0,0, - 255,255,255, - // 2 - 255,255,255, - 0,0,0, - 255,255,255, - 0,0,0, - // 1 - 0,0,0, - 255,255,255, - 0,0,0, - 255,255,255, - // 2 - 255,255,255, - 0,0,0, - 255,255,255, - 0,0,0, -}; - -const Text = struct { +const Texture = struct { texture: gl.Texture, - pub fn init(text: [:0]const u8) Text { + pub fn init(data: [*]const u8, width: usize, height: usize) Texture { var tex = gl.genTexture(); //defer gl.Texture.delete(tex); gl.bindTexture(tex, .@"2d"); - var font = sdl.ttf.openFont("res/fonts/MarginaliaRegular-8XlZ.ttf", 200) catch unreachable; - //defer font.close(); - var surface = font.renderTextBlended(text, sdl.Color.white) catch unreachable; - const width = @intCast(usize, surface.ptr.w); - const height = @intCast(usize, surface.ptr.h); - var newsurf = sdl.c.SDL_ConvertSurfaceFormat(surface.ptr, @enumToInt(sdl.PixelFormatEnum.argb8888), 0) orelse unreachable; - const data = @ptrCast([*]const u8, newsurf.pixels); - //defer surface.destroy(); - gl.textureImage2D(.@"2d", 0, .rgba, width, height, .rgba, .unsigned_byte, data); gl.texParameter(.@"2d", .wrap_s, .clamp_to_edge); @@ -263,12 +258,27 @@ const Text = struct { gl.texParameter(.@"2d", .min_filter, .linear); gl.texParameter(.@"2d", .mag_filter, .linear); - return Text { + return Texture{ .texture = tex, }; } - pub fn deinit(self: *Text) void { - _ = self; + pub fn fromText(text: [:0]const u8) Texture { + var font = sdl.ttf.openFont("res/fonts/MarginaliaRegular-8XlZ.ttf", 200) catch unreachable; + defer font.close(); + + var surface = font.renderTextBlended(text, sdl.Color.white) catch unreachable; + defer surface.destroy(); + + const width = @intCast(usize, surface.ptr.w); + const height = @intCast(usize, surface.ptr.h); + var newsurf = sdl.c.SDL_ConvertSurfaceFormat(surface.ptr, @enumToInt(sdl.PixelFormatEnum.argb8888), 0) orelse unreachable; + const data = @ptrCast([*]const u8, newsurf.pixels); + + return Texture.init(data, width, height); + } + + pub fn deinit(self: *Texture) void { + gl.Texture.delete(self.texture); } }; diff --git a/src/shaders/color.frag b/src/shaders/color.frag new file mode 100644 index 0000000..710359f --- /dev/null +++ b/src/shaders/color.frag @@ -0,0 +1,7 @@ +#version 330 core + +in vec4 color; + +void main(){ + gl_FragColor = color; +} diff --git a/src/shaders/vector.vs b/src/shaders/color.vert similarity index 53% rename from src/shaders/vector.vs rename to src/shaders/color.vert index b1c6563..2bdf9bd 100644 --- a/src/shaders/vector.vs +++ b/src/shaders/color.vert @@ -1,16 +1,12 @@ #version 330 core layout (location = 0) in vec2 pos; -layout (location = 1) in vec4 v_color; -layout (location = 2) in vec2 texcoord; -out vec2 Texcoord; +layout (location = 1) in vec4 vColor; +out vec4 color; uniform mat4 mvp; -out vec4 color; - void main() { - Texcoord = texcoord; - color = v_color; + color = vColor; gl_Position = mvp * vec4((pos + vec2(0.5, 0.5)), 0, 1); } diff --git a/src/shaders/fragment.fs b/src/shaders/texture.frag similarity index 51% rename from src/shaders/fragment.fs rename to src/shaders/texture.frag index 1153f1c..e25c68a 100644 --- a/src/shaders/fragment.fs +++ b/src/shaders/texture.frag @@ -1,9 +1,9 @@ #version 330 core +in vec2 texcoord; in vec4 color; -in vec2 Texcoord; uniform sampler2D tex; void main(){ - gl_FragColor = texture(tex, Texcoord) * color; + gl_FragColor = texture(tex, texcoord) * color; } diff --git a/src/shaders/texture.vert b/src/shaders/texture.vert new file mode 100644 index 0000000..c6225f7 --- /dev/null +++ b/src/shaders/texture.vert @@ -0,0 +1,15 @@ +#version 330 core + +layout (location = 0) in vec2 pos; +layout (location = 1) in vec4 vColor; +out vec4 color; +layout (location = 2) in vec2 vTexcoord; +out vec2 texcoord; + +uniform mat4 mvp; + +void main() { + texcoord = vTexcoord; + color = vColor; + gl_Position = mvp * vec4((pos + vec2(0.5, 0.5)), 0, 1); +}