mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
busted text rendering
This commit is contained in:
@ -1,9 +1,13 @@
|
|||||||
#version 120
|
#version 330 core
|
||||||
|
|
||||||
varying vec2 texcoord;
|
in vec2 TexCoords;
|
||||||
uniform sampler2D tex;
|
out vec4 color;
|
||||||
uniform vec4 color;
|
|
||||||
|
|
||||||
void main(void) {
|
uniform sampler2D text;
|
||||||
gl_FragColor = vec4(1, 1, 1, texture2D(tex, texcoord).r) * color;
|
uniform vec3 textColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);
|
||||||
|
color = vec4(textColor, 1.0) * sampled;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
#version 120
|
#version 330 core
|
||||||
|
|
||||||
attribute vec4 coord;
|
layout (location = 0) in vec4 vertex; // <vec2 pos, vec2 tex>
|
||||||
varying vec2 texcoord;
|
out vec2 TexCoords;
|
||||||
|
|
||||||
void main(void) {
|
void main()
|
||||||
gl_Position = vec4(coord.xy, 0, 1);
|
{
|
||||||
texcoord = coord.zw;
|
gl_Position = vec4(vertex.xy, 0.0, 1.0);
|
||||||
|
TexCoords = vertex.zw;
|
||||||
}
|
}
|
||||||
|
55
src/App.zig
55
src/App.zig
@ -12,17 +12,12 @@ const log = std.log;
|
|||||||
|
|
||||||
window: glfw.Window,
|
window: glfw.Window,
|
||||||
|
|
||||||
glprog: gl.Program,
|
text: TextRenderer,
|
||||||
vao: gl.VertexArray,
|
|
||||||
|
|
||||||
/// Initialize the main app instance. This creates the main window, sets
|
/// Initialize the main app instance. This creates the main window, sets
|
||||||
/// up the renderer state, compiles the shaders, etc. This is the primary
|
/// up the renderer state, compiles the shaders, etc. This is the primary
|
||||||
/// "startup" logic.
|
/// "startup" logic.
|
||||||
pub fn init(alloc: std.mem.Allocator) !App {
|
pub fn init(alloc: std.mem.Allocator) !App {
|
||||||
// Setup our text renderer
|
|
||||||
var texter = try TextRenderer.init(alloc);
|
|
||||||
defer texter.deinit();
|
|
||||||
|
|
||||||
// Create our window
|
// Create our window
|
||||||
const window = try glfw.Window.create(640, 480, "ghostty", null, null, .{
|
const window = try glfw.Window.create(640, 480, "ghostty", null, null, .{
|
||||||
.context_version_major = 3,
|
.context_version_major = 3,
|
||||||
@ -45,54 +40,22 @@ pub fn init(alloc: std.mem.Allocator) !App {
|
|||||||
}).callback);
|
}).callback);
|
||||||
|
|
||||||
// Blending for text
|
// Blending for text
|
||||||
|
gl.c.glEnable(gl.c.GL_CULL_FACE);
|
||||||
gl.c.glEnable(gl.c.GL_BLEND);
|
gl.c.glEnable(gl.c.GL_BLEND);
|
||||||
gl.c.glBlendFunc(gl.c.GL_SRC_ALPHA, gl.c.GL_ONE_MINUS_SRC_ALPHA);
|
gl.c.glBlendFunc(gl.c.GL_SRC_ALPHA, gl.c.GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
// Compile our shaders
|
// Setup our text renderer
|
||||||
const vs = try gl.Shader.create(gl.c.GL_VERTEX_SHADER);
|
var texter = try TextRenderer.init(alloc);
|
||||||
try vs.setSourceAndCompile(vs_source);
|
errdefer texter.deinit();
|
||||||
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();
|
|
||||||
|
|
||||||
return App{
|
return App{
|
||||||
.window = window,
|
.window = window,
|
||||||
.glprog = program,
|
.text = texter,
|
||||||
|
|
||||||
.vao = vao,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *App) void {
|
pub fn deinit(self: *App) void {
|
||||||
|
self.text.deinit();
|
||||||
self.window.destroy();
|
self.window.destroy();
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
@ -103,9 +66,7 @@ pub fn run(self: App) !void {
|
|||||||
gl.clearColor(0.2, 0.3, 0.3, 1.0);
|
gl.clearColor(0.2, 0.3, 0.3, 1.0);
|
||||||
gl.clear(gl.c.GL_COLOR_BUFFER_BIT);
|
gl.clear(gl.c.GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
try self.glprog.use();
|
try self.text.render("hello", 25.0, 25.0, 1.0, .{ 0.5, 0.8, 0.2 });
|
||||||
try self.vao.bind();
|
|
||||||
try gl.drawArrays(gl.c.GL_TRIANGLES, 0, 3);
|
|
||||||
|
|
||||||
try self.window.swapBuffers();
|
try self.window.swapBuffers();
|
||||||
try glfw.waitEvents();
|
try glfw.waitEvents();
|
||||||
|
@ -8,12 +8,15 @@ alloc: std.mem.Allocator,
|
|||||||
ft: ftc.FT_Library,
|
ft: ftc.FT_Library,
|
||||||
face: ftc.FT_Face,
|
face: ftc.FT_Face,
|
||||||
chars: CharList,
|
chars: CharList,
|
||||||
|
vao: gl.VertexArray = undefined,
|
||||||
|
vbo: gl.Buffer = undefined,
|
||||||
|
program: gl.Program = undefined,
|
||||||
|
|
||||||
const CharList = std.ArrayListUnmanaged(Char);
|
const CharList = std.ArrayListUnmanaged(Char);
|
||||||
const Char = struct {
|
const Char = struct {
|
||||||
tex: gl.Texture,
|
tex: gl.Texture,
|
||||||
size: @Vector(2, c_uint),
|
size: @Vector(2, f32),
|
||||||
bearing: @Vector(2, c_int),
|
bearing: @Vector(2, f32),
|
||||||
advance: c_uint,
|
advance: c_uint,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -82,22 +85,42 @@ pub fn init(alloc: std.mem.Allocator) !TextRenderer {
|
|||||||
chars.appendAssumeCapacity(.{
|
chars.appendAssumeCapacity(.{
|
||||||
.tex = tex,
|
.tex = tex,
|
||||||
.size = .{
|
.size = .{
|
||||||
face.*.glyph.*.bitmap.width,
|
@intToFloat(f32, face.*.glyph.*.bitmap.width),
|
||||||
face.*.glyph.*.bitmap.rows,
|
@intToFloat(f32, face.*.glyph.*.bitmap.rows),
|
||||||
},
|
},
|
||||||
.bearing = .{
|
.bearing = .{
|
||||||
face.*.glyph.*.bitmap_left,
|
@intToFloat(f32, face.*.glyph.*.bitmap_left),
|
||||||
face.*.glyph.*.bitmap_top,
|
@intToFloat(f32, face.*.glyph.*.bitmap_top),
|
||||||
},
|
},
|
||||||
.advance = @intCast(c_uint, face.*.glyph.*.advance.x),
|
.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{
|
return TextRenderer{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.ft = ft,
|
.ft = ft,
|
||||||
.face = face,
|
.face = face,
|
||||||
.chars = chars,
|
.chars = chars,
|
||||||
|
.program = program,
|
||||||
|
.vao = vao,
|
||||||
|
.vbo = vbo,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,4 +136,59 @@ pub fn deinit(self: *TextRenderer) void {
|
|||||||
self.* = undefined;
|
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");
|
const face_ttf = @embedFile("../fonts/Inconsolata-Regular.ttf");
|
||||||
|
@ -19,11 +19,40 @@ pub const Binding = struct {
|
|||||||
data: anytype,
|
data: anytype,
|
||||||
usage: c.GLenum,
|
usage: c.GLenum,
|
||||||
) !void {
|
) !void {
|
||||||
// Determine the size and pointer to the given data.
|
const info = dataInfo(data);
|
||||||
const info: struct {
|
c.glBufferData(b.target, info.size, info.ptr, usage);
|
||||||
size: isize,
|
try errors.getError();
|
||||||
ptr: *const anyopaque,
|
}
|
||||||
} = switch (@typeInfo(@TypeOf(data))) {
|
|
||||||
|
/// 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,
|
||||||
|
} {
|
||||||
|
return switch (@typeInfo(@TypeOf(data))) {
|
||||||
.Array => |ary| .{
|
.Array => |ary| .{
|
||||||
.size = @sizeOf(ary.child) * ary.len,
|
.size = @sizeOf(ary.child) * ary.len,
|
||||||
.ptr = &data,
|
.ptr = &data,
|
||||||
@ -47,9 +76,6 @@ pub const Binding = struct {
|
|||||||
unreachable;
|
unreachable;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
c.glBufferData(b.target, info.size, info.ptr, usage);
|
|
||||||
try errors.getError();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn enableVertexAttribArray(_: Binding, idx: c.GLuint) !void {
|
pub inline fn enableVertexAttribArray(_: Binding, idx: c.GLuint) !void {
|
||||||
|
@ -18,6 +18,25 @@ pub inline fn create() !Program {
|
|||||||
return Program{ .id = id };
|
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 {
|
pub inline fn attachShader(p: Program, s: Shader) !void {
|
||||||
c.glAttachShader(p.id, s.id);
|
c.glAttachShader(p.id, s.id);
|
||||||
try errors.getError();
|
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.
|
// Perform the correct call depending on the type of the value.
|
||||||
switch (@TypeOf(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]),
|
@Vector(4, f32) => c.glUniform4f(loc, value[0], value[1], value[2], value[3]),
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,10 @@ const errors = @import("errors.zig");
|
|||||||
|
|
||||||
id: c.GLuint,
|
id: c.GLuint,
|
||||||
|
|
||||||
|
pub inline fn active(target: c.GLenum) !void {
|
||||||
|
c.glActiveTexture(target);
|
||||||
|
}
|
||||||
|
|
||||||
pub const Binding = struct {
|
pub const Binding = struct {
|
||||||
target: c.GLenum,
|
target: c.GLenum,
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user