busted text rendering

This commit is contained in:
Mitchell Hashimoto
2022-04-04 09:43:46 -07:00
parent 670af17a1b
commit fc28b8c032
7 changed files with 167 additions and 73 deletions

View File

@ -1,9 +1,13 @@
#version 120
#version 330 core
varying vec2 texcoord;
uniform sampler2D tex;
uniform vec4 color;
in vec2 TexCoords;
out vec4 color;
void main(void) {
gl_FragColor = vec4(1, 1, 1, texture2D(tex, texcoord).r) * color;
uniform sampler2D text;
uniform vec3 textColor;
void main()
{
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);
color = vec4(textColor, 1.0) * sampled;
}

View File

@ -1,9 +1,10 @@
#version 120
#version 330 core
attribute vec4 coord;
varying vec2 texcoord;
layout (location = 0) in vec4 vertex; // <vec2 pos, vec2 tex>
out vec2 TexCoords;
void main(void) {
gl_Position = vec4(coord.xy, 0, 1);
texcoord = coord.zw;
void main()
{
gl_Position = vec4(vertex.xy, 0.0, 1.0);
TexCoords = vertex.zw;
}

View File

@ -12,17 +12,12 @@ const log = std.log;
window: glfw.Window,
glprog: gl.Program,
vao: gl.VertexArray,
text: TextRenderer,
/// Initialize the main app instance. This creates the main window, sets
/// up the renderer state, compiles the shaders, etc. This is the primary
/// "startup" logic.
pub fn init(alloc: std.mem.Allocator) !App {
// Setup our text renderer
var texter = try TextRenderer.init(alloc);
defer texter.deinit();
// Create our window
const window = try glfw.Window.create(640, 480, "ghostty", null, null, .{
.context_version_major = 3,
@ -45,54 +40,22 @@ pub fn init(alloc: std.mem.Allocator) !App {
}).callback);
// Blending for text
gl.c.glEnable(gl.c.GL_CULL_FACE);
gl.c.glEnable(gl.c.GL_BLEND);
gl.c.glBlendFunc(gl.c.GL_SRC_ALPHA, gl.c.GL_ONE_MINUS_SRC_ALPHA);
// Compile our shaders
const vs = try gl.Shader.create(gl.c.GL_VERTEX_SHADER);
try vs.setSourceAndCompile(vs_source);
errdefer vs.destroy();
const fs = try gl.Shader.create(gl.c.GL_FRAGMENT_SHADER);
try fs.setSourceAndCompile(fs_source);
errdefer fs.destroy();
// Link our shader program
const program = try gl.Program.create();
errdefer program.destroy();
try program.attachShader(vs);
try program.attachShader(fs);
try program.link();
vs.destroy();
fs.destroy();
// Create our bufer or vertices
const vertices = [_]f32{
-0.5, -0.5, 0.0, // left
0.5, -0.5, 0.0, // right
0.0, 0.5, 0.0, // top
};
const vao = try gl.VertexArray.create();
//defer vao.destroy();
const vbo = try gl.Buffer.create();
//defer vbo.destroy();
try vao.bind();
var binding = try vbo.bind(gl.c.GL_ARRAY_BUFFER);
try binding.setData(&vertices, gl.c.GL_STATIC_DRAW);
try binding.vertexAttribPointer(0, 3, gl.c.GL_FLOAT, false, 3 * @sizeOf(f32), null);
try binding.enableVertexAttribArray(0);
binding.unbind();
try gl.VertexArray.unbind();
// Setup our text renderer
var texter = try TextRenderer.init(alloc);
errdefer texter.deinit();
return App{
.window = window,
.glprog = program,
.vao = vao,
.text = texter,
};
}
pub fn deinit(self: *App) void {
self.text.deinit();
self.window.destroy();
self.* = undefined;
}
@ -103,9 +66,7 @@ pub fn run(self: App) !void {
gl.clearColor(0.2, 0.3, 0.3, 1.0);
gl.clear(gl.c.GL_COLOR_BUFFER_BIT);
try self.glprog.use();
try self.vao.bind();
try gl.drawArrays(gl.c.GL_TRIANGLES, 0, 3);
try self.text.render("hello", 25.0, 25.0, 1.0, .{ 0.5, 0.8, 0.2 });
try self.window.swapBuffers();
try glfw.waitEvents();

View File

@ -8,12 +8,15 @@ alloc: std.mem.Allocator,
ft: ftc.FT_Library,
face: ftc.FT_Face,
chars: CharList,
vao: gl.VertexArray = undefined,
vbo: gl.Buffer = undefined,
program: gl.Program = undefined,
const CharList = std.ArrayListUnmanaged(Char);
const Char = struct {
tex: gl.Texture,
size: @Vector(2, c_uint),
bearing: @Vector(2, c_int),
size: @Vector(2, f32),
bearing: @Vector(2, f32),
advance: c_uint,
};
@ -82,22 +85,42 @@ pub fn init(alloc: std.mem.Allocator) !TextRenderer {
chars.appendAssumeCapacity(.{
.tex = tex,
.size = .{
face.*.glyph.*.bitmap.width,
face.*.glyph.*.bitmap.rows,
@intToFloat(f32, face.*.glyph.*.bitmap.width),
@intToFloat(f32, face.*.glyph.*.bitmap.rows),
},
.bearing = .{
face.*.glyph.*.bitmap_left,
face.*.glyph.*.bitmap_top,
@intToFloat(f32, face.*.glyph.*.bitmap_left),
@intToFloat(f32, face.*.glyph.*.bitmap_top),
},
.advance = @intCast(c_uint, face.*.glyph.*.advance.x),
});
}
// Configure VAO/VBO for glyph rendering
const vao = try gl.VertexArray.create();
const vbo = try gl.Buffer.create();
try vao.bind();
var binding = try vbo.bind(gl.c.GL_ARRAY_BUFFER);
try binding.setDataType([6 * 4]f32, gl.c.GL_DYNAMIC_DRAW);
try binding.enableVertexAttribArray(0);
try binding.vertexAttribPointer(0, 4, gl.c.GL_FLOAT, false, 4 * @sizeOf(f32), null);
binding.unbind();
try gl.VertexArray.unbind();
// Create our shader
const program = try gl.Program.createVF(
@embedFile("../shaders/text.v.glsl"),
@embedFile("../shaders/text.f.glsl"),
);
return TextRenderer{
.alloc = alloc,
.ft = ft,
.face = face,
.chars = chars,
.program = program,
.vao = vao,
.vbo = vbo,
};
}
@ -113,4 +136,59 @@ pub fn deinit(self: *TextRenderer) void {
self.* = undefined;
}
pub fn render(
self: TextRenderer,
text: []const u8,
x: f32,
y: f32,
scale: f32,
color: @Vector(3, f32),
) !void {
try self.program.use();
try self.program.setUniform("textColor", color);
try gl.Texture.active(gl.c.GL_TEXTURE0);
try self.vao.bind();
std.log.info("---", .{});
var curx: f32 = x;
for (text) |c| {
const char = self.chars.items[c];
const xpos = curx + (char.bearing[0] * scale);
const ypos = y + (char.bearing[1] * scale);
const w = char.size[0] * scale;
const h = char.size[1] * scale;
std.log.info("CHARACTER INFO ch={} xpos={} ypos={} w={} h={}", .{
c,
xpos,
ypos,
w,
h,
});
const vert = [6][4]f32{
.{ xpos, ypos + h, 0.0, 0.0 },
.{ xpos, ypos, 0.0, 1.0 },
.{ xpos + w, ypos, 1.0, 1.0 },
.{ xpos, ypos + h, 0.0, 0.0 },
.{ xpos + w, ypos, 1.0, 1.0 },
.{ xpos + w, ypos + h, 1.0, 0.0 },
};
var texbind = try char.tex.bind(gl.c.GL_TEXTURE_2D);
defer texbind.unbind();
var bind = try self.vbo.bind(gl.c.GL_ARRAY_BUFFER);
try bind.setSubData(0, vert);
bind.unbind();
try gl.drawArrays(gl.c.GL_TRIANGLES, 0, 6);
curx += @intToFloat(f32, char.advance >> 6) * scale;
}
try gl.VertexArray.unbind();
}
const face_ttf = @embedFile("../fonts/Inconsolata-Regular.ttf");

View File

@ -19,11 +19,40 @@ pub const Binding = struct {
data: anytype,
usage: c.GLenum,
) !void {
// Determine the size and pointer to the given data.
const info: struct {
const info = dataInfo(data);
c.glBufferData(b.target, info.size, info.ptr, usage);
try errors.getError();
}
/// Sets the data of this bound buffer. The data can be any array-like
/// type. The size of the data is automatically determined based on the type.
pub inline fn setSubData(
b: Binding,
offset: usize,
data: anytype,
) !void {
const info = dataInfo(data);
c.glBufferSubData(b.target, @intCast(c_long, offset), info.size, info.ptr);
try errors.getError();
}
/// Sets the buffer data with a null buffer that is expected to be
/// filled in the future using subData. This requires the type just so
/// we can setup the data size.
pub inline fn setDataType(
b: Binding,
comptime T: type,
usage: c.GLenum,
) !void {
c.glBufferData(b.target, @sizeOf(T), null, usage);
try errors.getError();
}
fn dataInfo(data: anytype) struct {
size: isize,
ptr: *const anyopaque,
} = switch (@typeInfo(@TypeOf(data))) {
} {
return switch (@typeInfo(@TypeOf(data))) {
.Array => |ary| .{
.size = @sizeOf(ary.child) * ary.len,
.ptr = &data,
@ -47,9 +76,6 @@ pub const Binding = struct {
unreachable;
},
};
c.glBufferData(b.target, info.size, info.ptr, usage);
try errors.getError();
}
pub inline fn enableVertexAttribArray(_: Binding, idx: c.GLuint) !void {

View File

@ -18,6 +18,25 @@ pub inline fn create() !Program {
return Program{ .id = id };
}
/// Create a program from a vertex and fragment shader source. This will
/// compile and link the vertex and fragment shader.
pub inline fn createVF(vsrc: [:0]const u8, fsrc: [:0]const u8) !Program {
const vs = try Shader.create(c.GL_VERTEX_SHADER);
try vs.setSourceAndCompile(vsrc);
defer vs.destroy();
const fs = try Shader.create(c.GL_FRAGMENT_SHADER);
try fs.setSourceAndCompile(fsrc);
defer fs.destroy();
const p = try create();
try p.attachShader(vs);
try p.attachShader(fs);
try p.link();
return p;
}
pub inline fn attachShader(p: Program, s: Shader) !void {
c.glAttachShader(p.id, s.id);
try errors.getError();
@ -55,6 +74,7 @@ pub inline fn setUniform(p: Program, n: [:0]const u8, value: anytype) !void {
// Perform the correct call depending on the type of the value.
switch (@TypeOf(value)) {
@Vector(3, f32) => c.glUniform3f(loc, value[0], value[1], value[2]),
@Vector(4, f32) => c.glUniform4f(loc, value[0], value[1], value[2], value[3]),
else => unreachable,
}

View File

@ -6,6 +6,10 @@ const errors = @import("errors.zig");
id: c.GLuint,
pub inline fn active(target: c.GLenum) !void {
c.glActiveTexture(target);
}
pub const Binding = struct {
target: c.GLenum,