outline font

This commit is contained in:
Zack Buhman 2026-03-19 22:36:16 -05:00
parent ce7c4c4cc6
commit 16c0123627
17 changed files with 306 additions and 75 deletions

View File

@ -28,7 +28,8 @@ OBJS = \
src/gl.o \ src/gl.o \
src/opengl.o \ src/opengl.o \
src/test.o \ src/test.o \
src/font.o \ src/font/bitmap.o \
src/font/outline.o \
src/window.o \ src/window.o \
src/bresenham.o \ src/bresenham.o \
src/file.o \ src/file.o \

Binary file not shown.

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
namespace font { namespace font::bitmap {
struct font_desc { struct font_desc {
char const * const path; char const * const path;
@ -12,35 +12,35 @@ namespace font {
font_desc const terminus[] = { font_desc const terminus[] = {
{ {
.path = "font/terminus_128x64_6x12.data", .path = "font/bitmap/terminus_128x64_6x12.data",
.texture_width = 128, .texture_width = 128,
.texture_height = 64, .texture_height = 64,
.glyph_width = 6, .glyph_width = 6,
.glyph_height = 12, .glyph_height = 12,
}, },
{ {
.path = "font/terminus_128x128_8x16.data", .path = "font/bitmap/terminus_128x128_8x16.data",
.texture_width = 128, .texture_width = 128,
.texture_height = 128, .texture_height = 128,
.glyph_width = 8, .glyph_width = 8,
.glyph_height = 16, .glyph_height = 16,
}, },
{ {
.path = "font/terminus_256x128_10x18.data", .path = "font/bitmap/terminus_256x128_10x18.data",
.texture_width = 256, .texture_width = 256,
.texture_height = 128, .texture_height = 128,
.glyph_width = 10, .glyph_width = 10,
.glyph_height = 18, .glyph_height = 18,
}, },
{ {
.path = "font/terminus_256x128_12x24.data", .path = "font/bitmap/terminus_256x128_12x24.data",
.texture_width = 256, .texture_width = 256,
.texture_height = 128, .texture_height = 128,
.glyph_width = 12, .glyph_width = 12,
.glyph_height = 24, .glyph_height = 24,
}, },
{ {
.path = "font/terminus_256x256_16x32.data", .path = "font/bitmap/terminus_256x256_16x32.data",
.texture_width = 256, .texture_width = 256,
.texture_height = 256, .texture_height = 256,
.glyph_width = 16, .glyph_width = 16,
@ -60,7 +60,6 @@ namespace font {
}; };
void load_shader(); void load_shader();
font load_font(font_desc const& desc);
void load_fonts(font * const fonts, font_desc const * const descs, int length); void load_fonts(font * const fonts, font_desc const * const descs, int length);
int best_font(font_desc const * const descs, int length); int best_font(font_desc const * const descs, int length);
void draw_start(font const& font, unsigned int vertex_array_object, unsigned int index_buffer); void draw_start(font const& font, unsigned int vertex_array_object, unsigned int index_buffer);

29
include/font/outline.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include "outline_types.h"
namespace font::outline {
struct font_desc {
char const * const path;
};
font_desc const uncial_antiqua[] = {
{
.path = "font/outline/uncial_antiqua_36.data",
},
};
int const uncial_antiqua_length = (sizeof (uncial_antiqua)) / (sizeof (font_desc));
struct font {
unsigned int texture;
types::font const * font;
types::glyph const * glyphs;
};
void load_shader();
void load_fonts(font * const fonts, font_desc const * const descs, int length);
void draw_start(font const& font, unsigned int vertex_array_object, unsigned int index_buffer);
int draw_string(font const& font, char const * const s, int x, int y);
}

View File

@ -0,0 +1,49 @@
// this file is designed to be platform-agnostic
#pragma once
#include <stdint.h>
namespace font::outline::types {
// metrics are 26.6 fixed point
struct glyph_metrics {
int32_t horiBearingX;
int32_t horiBearingY;
int32_t horiAdvance;
} __attribute__ ((packed));
static_assert((sizeof (struct glyph_metrics)) == ((sizeof (int32_t)) * 3));
struct glyph_bitmap {
uint16_t x;
uint16_t y;
uint16_t width;
uint16_t height;
} __attribute__ ((packed));
static_assert((sizeof (struct glyph_bitmap)) == ((sizeof (uint16_t)) * 4));
struct glyph {
struct glyph_bitmap bitmap;
struct glyph_metrics metrics;
} __attribute__ ((packed));
static_assert((sizeof (struct glyph)) == ((sizeof (struct glyph_bitmap)) + (sizeof (struct glyph_metrics))));
struct font {
uint32_t first_char_code;
uint32_t last_char_code;
struct face_metrics {
int32_t height; // 26.6 fixed point
int32_t max_advance; // 26.6 fixed point
} face_metrics;
uint16_t glyph_count;
uint16_t _texture_stride;
uint16_t texture_width;
uint16_t texture_height;
uint32_t max_z_curve_ix;
} __attribute__ ((packed));
static_assert((sizeof (struct font)) == ((sizeof (uint32_t)) * 7));
}

View File

@ -1,29 +1,11 @@
#version 330 core #version 430 core
const vec2 vtx[4] = vec2[](vec2(-1.0, 1.0), // tl const vec2 vtx[4] = vec2[](vec2(-1.0, 1.0), // tl
vec2( 1.0, 1.0), // tr vec2( 1.0, 1.0), // tr
vec2( 1.0, -1.0), // br vec2( 1.0, -1.0), // br
vec2(-1.0, -1.0)); // bl vec2(-1.0, -1.0)); // bl
/* layout (location = 0) uniform mat4 Transform;
tl tr
br
0 1 2
tr tl br
1 0 2
tl
bl br
2 1 3
br tl bl
2 0 3
1 0 2 3
*/
uniform mat4 Transform;
out vec4 PixelTexture; out vec4 PixelTexture;

17
shader/font_outline.frag Normal file
View File

@ -0,0 +1,17 @@
#version 430 core
layout (location = 1) uniform vec2 TextureSize;
layout (location = 2) uniform vec4 WidthHeightXY;
layout (location = 3, binding = 0) uniform sampler2D TextureSampler;
out vec4 g_color;
in vec4 PixelTexture;
void main()
{
vec2 coord = (PixelTexture.xy * WidthHeightXY.xy + WidthHeightXY.zw) * TextureSize;
vec4 color = texture(TextureSampler, coord);
g_color = vec4(color.x);
}

View File

@ -7,10 +7,10 @@
#include "opengl.h" #include "opengl.h"
#include "file.h" #include "file.h"
#include "font.h" #include "font/bitmap.h"
#include "window.h" #include "window.h"
namespace font { namespace font::bitmap {
struct location { struct location {
struct { struct {
@ -46,7 +46,7 @@ namespace font {
font_program = program; font_program = program;
} }
font load_font(font_desc const& desc) static inline font load_font(font_desc const& desc)
{ {
unsigned int texture; unsigned int texture;
glGenTextures(1, &texture); glGenTextures(1, &texture);

150
src/font/outline.cpp Normal file
View File

@ -0,0 +1,150 @@
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include "directxmath/directxmath.h"
#include "glad/gl.h"
#include "opengl.h"
#include "file.h"
#include "font/outline.h"
#include "font/outline_types.h"
#include "window.h"
namespace font::outline {
struct layout {
struct {
unsigned int transform;
unsigned int texture_size;
unsigned int width_height_xy;
unsigned int texture_sampler;
} uniform;
};
static layout const layout = {
.uniform = {
.transform = 0,
.texture_size = 1,
.width_height_xy = 2,
.texture_sampler = 3,
}
};
static unsigned int font_program = -1;
void load_shader()
{
unsigned int program = compile_from_files("shader/font.vert",
NULL, // geom
"shader/font_outline.frag");
font_program = program;
}
static inline font load_font(font_desc const& desc)
{
int font_data_size;
void * font_data = read_file(desc.path, &font_data_size);
assert(font_data != nullptr);
types::font * font = (types::font *)font_data;
types::glyph * glyphs = (types::glyph *)(((ptrdiff_t)font_data) + (sizeof (types::font)));
void * texture_data = (void *)(((ptrdiff_t)glyphs) + (sizeof (types::glyph)) * font->glyph_count);
ptrdiff_t font_end = ((ptrdiff_t)font_data) + font_data_size;
int texture_size = font->texture_width * font->texture_height;
assert(font_end - ((ptrdiff_t)texture_data) == texture_size);
unsigned int texture;
glGenTextures(1, &texture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
int width = font->texture_width;
int height = font->texture_height;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, texture_data);
glBindTexture(GL_TEXTURE_2D, 0);
return (outline::font){
.texture = texture,
.font = font,
.glyphs = glyphs,
};
}
void load_fonts(font * const fonts, font_desc const * const descs, int length)
{
for (int i = 0; i < length; i++) {
fonts[i] = load_font(descs[i]);
}
}
void draw_start(font const& font, unsigned int vertex_array_object, unsigned int index_buffer)
{
glUseProgram(font_program);
glDepthFunc(GL_ALWAYS);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, font.texture);
glBindVertexArray(vertex_array_object);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer);
XMFLOAT2 texture_size = {1.0f / font.font->texture_width, 1.0f / font.font->texture_height};
glUniform2fv(layout.uniform.texture_size, 1, (float *)&texture_size);
}
inline static XMFLOAT4X4 glyph_transform(float width, float height, float x, float y)
{
XMMATRIX transform
= XMMatrixScaling(width, height, 0)
* XMMatrixTranslation(x, -y, 0)
* XMMatrixScaling(2.0f / window::width, 2.0f / window::height, 0)
* XMMatrixTranslation(-1, 1, 0);
XMFLOAT4X4 transformf;
XMStoreFloat4x4(&transformf, transform);
return transformf;
}
int draw_string(font const& font, char const * const s, int x, int y)
{
int advance = 0;
const float fp = 1.0f / 64.0f;
int i = 0;
while (s[i] != 0) {
char c = s[i++];
if (!(c >= 0x20 && c <= 0x7f))
continue;
types::glyph const & glyph = font.glyphs[c - 0x20];
if (c > 0x20 && c <= 0x7f) {
XMFLOAT4 width_height_xy = {
(float)glyph.bitmap.width, (float)glyph.bitmap.height,
(float)glyph.bitmap.x, (float)glyph.bitmap.y,
};
XMFLOAT4X4 transform = glyph_transform(glyph.bitmap.width, glyph.bitmap.height,
x + ((float)(advance + glyph.metrics.horiBearingX) * fp),
y - ((float)(glyph.metrics.horiBearingY) * fp));
glUniform4fv(layout.uniform.width_height_xy, 1, (float *)&width_height_xy);
glUniformMatrix4fv(layout.uniform.transform, 1, GL_FALSE, (float *)&transform);
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, (void *)0);
}
advance += glyph.metrics.horiAdvance;
}
return x + (advance >> 6);
}
}

View File

@ -3,19 +3,18 @@
#include "directxmath/directxmath.h" #include "directxmath/directxmath.h"
#include "font.h" #include "font/bitmap.h"
#include "view.h" #include "view.h"
extern font::font * terminus_fonts; extern font::bitmap::font * terminus_fonts;
extern unsigned int empty_vertex_array_object; extern unsigned int empty_vertex_array_object;
extern unsigned int quad_index_buffer; extern unsigned int quad_index_buffer;
extern float current_time; extern float current_time;
extern float last_frame_time; extern float last_frame_time;
// depends on: // depends on:
// - font::load // - font::bitmap::load
// - load_quad_program // - load_quad_program
// - load_quad_index_buffer // - load_quad_index_buffer
// - empty_vertex_array_object // - empty_vertex_array_object
@ -34,10 +33,10 @@ namespace hud {
buf[label_length + len] = 0; buf[label_length + len] = 0;
} }
inline static float draw_vector(font::font const& ter_best, char * const buf, float y, char const * const label, XMVECTOR vec) inline static float draw_vector(font::bitmap::font const& ter_best, char * const buf, float y, char const * const label, XMVECTOR vec)
{ {
labeled_value<float>(buf, label, ": %5.2f %5.2f %5.2f %5.2f", XMVectorGetX(vec), XMVectorGetY(vec), XMVectorGetZ(vec), XMVectorGetW(vec)); labeled_value<float>(buf, label, ": %5.2f %5.2f %5.2f %5.2f", XMVectorGetX(vec), XMVectorGetY(vec), XMVectorGetZ(vec), XMVectorGetW(vec));
font::draw_string(ter_best, buf, 10, y); font::bitmap::draw_string(ter_best, buf, 10, y);
y += ter_best.desc->glyph_height; y += ter_best.desc->glyph_height;
return y; return y;
} }
@ -73,48 +72,42 @@ namespace hud {
return rolling_sum * (1.0f / 16.0f); return rolling_sum * (1.0f / 16.0f);
} }
template <typename T, typename... Args>
static float draw_label(font::bitmap::font const& font, char * buf, float x, float y, char const * const label, char const * const format, Args... args)
{
labeled_value<T>(buf, label, format, args...);
font::bitmap::draw_string(font, buf, x, y);
return y + font.desc->glyph_height;
}
void draw() void draw()
{ {
char buf[512]; static char buf[512];
float y = 10.0f; float y = 10.0f;
int font_ix = font::best_font(font::terminus, font::terminus_length); int font_ix = font::bitmap::best_font(font::bitmap::terminus, font::bitmap::terminus_length);
font::font const& ter_best = terminus_fonts[font_ix]; font::bitmap::font const& ter_best = terminus_fonts[font_ix];
font::draw_start(ter_best, empty_vertex_array_object, quad_index_buffer); font::bitmap::draw_start(ter_best, empty_vertex_array_object, quad_index_buffer);
labeled_value<float>(buf, "fov: ", "%.3f", view::state.fov); y = draw_label<float>(ter_best, buf, 10, y, "fov: ", "%.3f", view::state.fov);
font::draw_string(ter_best, buf, 10, y); y = draw_label<int>(ter_best, buf, 10, y, "font_height: ", "%d", ter_best.desc->glyph_height);
y += ter_best.desc->glyph_height;
labeled_value<int>(buf, "font_height: ", "%d", ter_best.desc->glyph_height);
font::draw_string(ter_best, buf, 10, y);
y += ter_best.desc->glyph_height;
/* /*
labeled_value<float>(buf, "lighting.quadratic: ", "%.2f", lighting.quadratic); y = draw_label<float>(ter_best, buf, 10, y, "lighting.quadratic: ", "%.2f", lighting.quadratic);
font::draw_string(ter_best, buf, 10, y); y = draw_label<float>(ter_best, buf, 10, y, "lighting.linear: ", "%.2f", lighting.linear);
y += ter_best.desc->glyph_height;
labeled_value<float>(buf, "lighting.linear: ", "%.2f", lighting.linear);
font::draw_string(ter_best, buf, 10, y);
y += ter_best.desc->glyph_height;
*/ */
y = draw_vector(ter_best, buf, y, "eye", XMVectorSetW(view::state.eye, 0)); y = draw_vector(ter_best, buf, y, "eye", XMVectorSetW(view::state.eye, 0));
y = draw_vector(ter_best, buf, y, "at", XMVectorSetW(view::state.at, 0)); y = draw_vector(ter_best, buf, y, "at", XMVectorSetW(view::state.at, 0));
y = draw_vector(ter_best, buf, y, "forward", XMVectorSetW(view::state.forward, 0)); y = draw_vector(ter_best, buf, y, "forward", XMVectorSetW(view::state.forward, 0));
labeled_value<float>(buf, "pitch: ", "%.4f", view::state.pitch); y = draw_label<float>(ter_best, buf, 10, y, "pitch: ", "%.4f", view::state.pitch);
font::draw_string(ter_best, buf, 10, y); y = draw_label<float>(ter_best, buf, 10, y, "frame_rate_avg: ", "%.2f", 1.0f / update_average(current_time - last_frame_time));
font::bitmap::draw_string(ter_best, "mouse:", 10, y);
y += ter_best.desc->glyph_height; y += ter_best.desc->glyph_height;
labeled_value<float>(buf, "frame_rate_avg: ", "%.2f", 1.0f / update_average(current_time - last_frame_time));
font::draw_string(ter_best, buf, 10, y);
y += ter_best.desc->glyph_height;
font::draw_string(ter_best, "mouse:", 10, y);
y += ter_best.desc->glyph_height;
y = draw_vector(ter_best, buf, y, " position", XMLoadFloat4((XMFLOAT4*)mouse_position)); y = draw_vector(ter_best, buf, y, " position", XMLoadFloat4((XMFLOAT4*)mouse_position));
y = draw_vector(ter_best, buf, y, " block", XMLoadFloat4((XMFLOAT4*)mouse_block)); y = draw_vector(ter_best, buf, y, " block", XMLoadFloat4((XMFLOAT4*)mouse_block));
} }

View File

@ -1,24 +1,24 @@
#include "font.h" #include "font/bitmap.h"
#include "pixel_line_art.h" #include "pixel_line_art.h"
#include "lua_api.h" #include "lua_api.h"
extern font::font * terminus_fonts; extern font::bitmap::font * terminus_fonts;
extern unsigned int empty_vertex_array_object; extern unsigned int empty_vertex_array_object;
extern unsigned int quad_index_buffer; extern unsigned int quad_index_buffer;
int draw_font_start() int draw_font_start()
{ {
int font_ix = font::best_font(font::terminus, font::terminus_length); int font_ix = font::bitmap::best_font(font::bitmap::terminus, font::bitmap::terminus_length);
font::font const& ter_best = terminus_fonts[font_ix]; font::bitmap::font const& ter_best = terminus_fonts[font_ix];
font::draw_start(ter_best, empty_vertex_array_object, quad_index_buffer); font::bitmap::draw_start(ter_best, empty_vertex_array_object, quad_index_buffer);
return font_ix; return font_ix;
} }
int draw_font(int font_ix, char const * text, int x, int y) int draw_font(int font_ix, char const * text, int x, int y)
{ {
font::font const& ter_best = terminus_fonts[font_ix]; font::bitmap::font const& ter_best = terminus_fonts[font_ix];
font::draw_string(ter_best, text, x, y); font::bitmap::draw_string(ter_best, text, x, y);
return ter_best.desc->glyph_height; return ter_best.desc->glyph_height;
} }

View File

@ -7,7 +7,8 @@
#include "opengl.h" #include "opengl.h"
#include "directxmath/directxmath.h" #include "directxmath/directxmath.h"
#include "test.h" #include "test.h"
#include "font.h" #include "font/bitmap.h"
#include "font/outline.h"
#include "window.h" #include "window.h"
#include "bresenham.h" #include "bresenham.h"
#include "file.h" #include "file.h"
@ -26,6 +27,7 @@
#include "collada/instance_types.h" #include "collada/instance_types.h"
#include "pixel_line_art.h" #include "pixel_line_art.h"
#include "flame.h" #include "flame.h"
#include "new.h"
#include "world/entry_table.h" #include "world/entry_table.h"
#include "world/world.h" #include "world/world.h"
@ -67,7 +69,8 @@ unsigned int quad_index_buffer = -1;
float current_time; float current_time;
float last_frame_time; float last_frame_time;
font::font * terminus_fonts; font::bitmap::font * terminus_fonts;
font::outline::font * uncial_antiqua_fonts;
geometry_buffer<4> geometry_buffer_pnc = {}; geometry_buffer<4> geometry_buffer_pnc = {};
static target_type const geometry_buffer_pnc_types[4] = { static target_type const geometry_buffer_pnc_types[4] = {
@ -142,10 +145,13 @@ void load(const char * source_path)
// font // font
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
font::load_shader(); font::bitmap::load_shader();
terminus_fonts = New<font::bitmap::font>(font::bitmap::terminus_length);
font::bitmap::load_fonts(terminus_fonts, font::bitmap::terminus, font::bitmap::terminus_length);
terminus_fonts = (font::font *)malloc((sizeof (font::font)) * font::terminus_length); font::outline::load_shader();
font::load_fonts(terminus_fonts, font::terminus, font::terminus_length); uncial_antiqua_fonts = New<font::outline::font>(font::outline::uncial_antiqua_length);
font::outline::load_fonts(uncial_antiqua_fonts, font::outline::uncial_antiqua, font::outline::uncial_antiqua_length);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// pixel_line_art // pixel_line_art
@ -439,6 +445,11 @@ void draw()
minecraft::current_world->light_count); minecraft::current_world->light_count);
//draw_quad(); //draw_quad();
hud::draw(); hud::draw();
//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//font::outline::draw_start(uncial_antiqua_fonts[0], empty_vertex_array_object, quad_index_buffer);
//font::outline::draw_string(uncial_antiqua_fonts[0], "test", 150, 500);
} else { } else {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);