WIP2: Text/texture rendering

This commit is contained in:
Dendy 2023-01-02 02:28:57 +01:00
parent ebe6aa8d9a
commit 9d23bbc39e
5 changed files with 151 additions and 123 deletions

View File

@ -8,18 +8,24 @@ const Self = @This();
window: sdl.Window, window: sdl.Window,
context: sdl.gl.Context, context: sdl.gl.Context,
//color_loc: u32,
buffer: gl.Buffer, 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, // The index of the program is the enum converted to int
//colors: [max_objects][4]f32 = .{[4]f32{ 0.0, 0.0, 0.0, 1.0 }} ** max_objects, programs: [nPrograms]gl.Program = .{undefined} ** nPrograms,
vbo_index: usize = 0,
const max_objects: usize = 16384; 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 { fn glGetProcAddress(p: []const u8, proc: [:0]const u8) ?*const anyopaque {
_ = p; _ = p;
return sdl.c.SDL_GL_GetProcAddress(@ptrCast([*c]const u8, proc)); return sdl.c.SDL_GL_GetProcAddress(@ptrCast([*c]const u8, proc));
@ -43,57 +49,30 @@ pub fn init() !Self {
.{ .context = .opengl, .vis = .shown }, .{ .context = .opengl, .vis = .shown },
); );
const proc: []const u8 = undefined;
try gl.loadExtensions(proc, glGetProcAddress);
const ctx = try sdl.gl.createContext(window); const ctx = try sdl.gl.createContext(window);
sdl.gl.setSwapInterval(.immediate) catch { sdl.gl.setSwapInterval(.immediate) catch {
std.debug.print("WARNING: Unable to configure the swap interval.\n", .{}); std.debug.print("WARNING: Unable to configure the swap interval.\n", .{});
}; };
const proc: []const u8 = undefined; gl.enable(.blend);
try gl.loadExtensions(proc, glGetProcAddress); gl.blendFunc(.src_alpha, .one_minus_src_alpha);
// Shader stuff var buf = gl.Buffer.gen();
var mvp_loc: u32 = undefined; buf.bind(.array_buffer);
const 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/vector.vs")}); var renderer = Self{ .window = window, .context = ctx, .buffer = buf };
fs.source(1, &.{@embedFile("shaders/fragment.fs")});
vs.compile(); // Load the shader programs according to the names in the enum
fs.compile(); inline for (std.meta.fields(shaderProgram)) |programEnum| {
renderer.programs[programEnum.value] = renderer.loadProgram(programEnum.name);
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});
} }
var vertex_array = gl.VertexArray.gen(); var vertex_array = gl.VertexArray.gen();
vertex_array.bind(); 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.vertexAttribPointer(0, 2, .float, false, @sizeOf(f32) * 8, @sizeOf(f32) * 0);
gl.enableVertexAttribArray(0); gl.enableVertexAttribArray(0);
@ -105,31 +84,74 @@ pub fn init() !Self {
gl.clearColor(0.91, 0.85, 0.65, 1.00); 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 var text = Texture.fromText("testing123");
gl.enable(.blend); _ = text;
gl.blendFunc(.src_alpha, .one_minus_src_alpha); //text.deinit();
var text = Text.init("testing123"); return renderer;
text.deinit(); }
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 { pub fn render(self: *Self) void {
//std.debug.print("Number of rendered elements: {}\n", .{self.test_counter}); // Render with each of the defined shader programs
self.test_counter = 0; 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); sdl.gl.swapWindow(self.window);
gl.clear(.{ .color = true }); 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 { 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 { pub fn fillRectangleEx(self: *Self, x: i32, y: i32, w: i32, h: i32, skew_x: i32) void {
// TODO: Is this still necessary? const program_i = @enumToInt(shaderProgram.texture);
self.test_counter += 1;
var xf = @intToFloat(f32, x); var xf = @intToFloat(f32, x);
var yf = @intToFloat(f32, y); 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 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{ const vertex_data = [_]f32{
xf + skew_x_offset, yf, // up-left xf + skew_x_offset, yf, // up-left
self.color[0], self.color[1], self.color[2], self.color[3], self.color[0], self.color[1],
0.0, 0.0, self.color[2], self.color[3],
0.0, 0.0,
xf + wf + skew_x_offset, yf, // up-right xf + wf + skew_x_offset, yf, // up-right
self.color[0], self.color[1], self.color[2], self.color[3], self.color[0], self.color[1],
1.0, 0.0, self.color[2], self.color[3],
1.0, 0.0,
xf - skew_x_offset, yf + hf, // down-left xf - skew_x_offset, yf + hf, // down-left
self.color[0], self.color[1], self.color[2], self.color[3], self.color[0], self.color[1],
0.0, 1.0, self.color[2], self.color[3],
0.0, 1.0,
xf + wf - skew_x_offset, yf + hf, // down-right xf + wf - skew_x_offset, yf + hf, // down-right
self.color[0], self.color[1], self.color[2], self.color[3], self.color[0], self.color[1],
1.0, 1.0, self.color[2], self.color[3],
1.0, 1.0,
xf + wf + skew_x_offset, yf, // up-right xf + wf + skew_x_offset, yf, // up-right
self.color[0], self.color[1], self.color[2], self.color[3], self.color[0], self.color[1],
1.0, 0.0, self.color[2], self.color[3],
1.0, 0.0,
xf - skew_x_offset, yf + hf, // down-left xf - skew_x_offset, yf + hf, // down-left
self.color[0], self.color[1], self.color[2], self.color[3], self.color[0], self.color[1],
0.0, 1.0, self.color[2], self.color[3],
0.0, 1.0,
}; };
std.mem.copy(f32, self.vbo[i .. i + vertex_data.len], &vertex_data); std.mem.copy(f32, self.vbo[program_i][i..], &vertex_data);
self.vbo_index += vertex_data.len; self.vbo_index[program_i] += vertex_data.len;
} }
fn renderRectangle(self: *Self, x: i32, y: i32, w: i32, h: i32) void { 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{ const Texture = struct {
// 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 {
texture: gl.Texture, 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(); var tex = gl.genTexture();
//defer gl.Texture.delete(tex); //defer gl.Texture.delete(tex);
gl.bindTexture(tex, .@"2d"); 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.textureImage2D(.@"2d", 0, .rgba, width, height, .rgba, .unsigned_byte, data);
gl.texParameter(.@"2d", .wrap_s, .clamp_to_edge); gl.texParameter(.@"2d", .wrap_s, .clamp_to_edge);
@ -263,12 +258,27 @@ const Text = struct {
gl.texParameter(.@"2d", .min_filter, .linear); gl.texParameter(.@"2d", .min_filter, .linear);
gl.texParameter(.@"2d", .mag_filter, .linear); gl.texParameter(.@"2d", .mag_filter, .linear);
return Text { return Texture{
.texture = tex, .texture = tex,
}; };
} }
pub fn deinit(self: *Text) void { pub fn fromText(text: [:0]const u8) Texture {
_ = self; 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);
} }
}; };

7
src/shaders/color.frag Normal file
View File

@ -0,0 +1,7 @@
#version 330 core
in vec4 color;
void main(){
gl_FragColor = color;
}

View File

@ -1,16 +1,12 @@
#version 330 core #version 330 core
layout (location = 0) in vec2 pos; layout (location = 0) in vec2 pos;
layout (location = 1) in vec4 v_color; layout (location = 1) in vec4 vColor;
layout (location = 2) in vec2 texcoord; out vec4 color;
out vec2 Texcoord;
uniform mat4 mvp; uniform mat4 mvp;
out vec4 color;
void main() { void main() {
Texcoord = texcoord; color = vColor;
color = v_color;
gl_Position = mvp * vec4((pos + vec2(0.5, 0.5)), 0, 1); gl_Position = mvp * vec4((pos + vec2(0.5, 0.5)), 0, 1);
} }

View File

@ -1,9 +1,9 @@
#version 330 core #version 330 core
in vec2 texcoord;
in vec4 color; in vec4 color;
in vec2 Texcoord;
uniform sampler2D tex; uniform sampler2D tex;
void main(){ void main(){
gl_FragColor = texture(tex, Texcoord) * color; gl_FragColor = texture(tex, texcoord) * color;
} }

15
src/shaders/texture.vert Normal file
View File

@ -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);
}