169 lines
4.9 KiB
C++
169 lines
4.9 KiB
C++
#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.h"
|
|
#include "window.h"
|
|
|
|
namespace font {
|
|
|
|
struct location {
|
|
struct {
|
|
unsigned int transform;
|
|
unsigned int texture_sampler;
|
|
unsigned int cell;
|
|
unsigned int glyph;
|
|
} uniform;
|
|
};
|
|
|
|
static location location;
|
|
|
|
static unsigned int font_program = -1;
|
|
|
|
void load_shader()
|
|
{
|
|
unsigned int program = compile_from_files("shader/font.vert",
|
|
NULL, // geom
|
|
"shader/font.frag");
|
|
|
|
location.uniform.transform = glGetUniformLocation(program, "Transform");
|
|
location.uniform.texture_sampler = glGetUniformLocation(program, "TextureSampler");
|
|
location.uniform.cell = glGetUniformLocation(program, "Cell");
|
|
location.uniform.glyph = glGetUniformLocation(program, "Glyph");
|
|
printf("font program:\n");
|
|
printf(" uniforms:\n transform %u\n texture_sampler %u\n cell %u\n glyph %u\n",
|
|
location.uniform.transform,
|
|
location.uniform.texture_sampler,
|
|
location.uniform.cell,
|
|
location.uniform.glyph
|
|
);
|
|
|
|
font_program = program;
|
|
}
|
|
|
|
font load_font(font_desc const& desc)
|
|
{
|
|
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 texture_data_size;
|
|
void * texture_data = read_file(desc.path, &texture_data_size);
|
|
assert(texture_data != nullptr);
|
|
|
|
int width = desc.texture_width;
|
|
int height = desc.texture_height;
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, texture_data);
|
|
|
|
free(texture_data);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
return (font){
|
|
.desc = &desc,
|
|
.texture = texture,
|
|
.stride = desc.texture_width / desc.glyph_width,
|
|
.cell = { (float)desc.glyph_width / (float)desc.texture_width,
|
|
(float)desc.glyph_height / (float)desc.texture_height },
|
|
};
|
|
}
|
|
|
|
static inline int min(int a, int b)
|
|
{
|
|
return a < b ? a : b;
|
|
}
|
|
|
|
int best_font(font_desc const * const descs, int length)
|
|
{
|
|
int dimension = min(window::width, window::height);
|
|
int ideal_height = (16 * dimension) / 1024;
|
|
//printf("ideal_height: %d\n", ideal_height);
|
|
int nearest = dimension;
|
|
int nearest_ix = -1;
|
|
for (int i = 0; i < length; i++) {
|
|
font_desc const& desc = descs[i];
|
|
int distance = abs(desc.glyph_height - ideal_height);
|
|
if (distance < nearest) {
|
|
nearest = distance;
|
|
nearest_ix = i;
|
|
}
|
|
}
|
|
assert(nearest_ix != -1);
|
|
//printf("selected %d\n", descs[nearest_ix].glyph_height);
|
|
return nearest_ix;
|
|
}
|
|
|
|
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]);
|
|
}
|
|
}
|
|
|
|
inline static XMFLOAT2 glyph_coordinate(font const& font, int ord)
|
|
{
|
|
int c = ord - 32;
|
|
int x = c % font.stride;
|
|
int y = c / font.stride;
|
|
XMVECTOR coord = XMVectorSet(x, y, 0, 0);
|
|
XMFLOAT2 coordf;
|
|
XMStoreFloat2(&coordf, coord);
|
|
return coordf;
|
|
}
|
|
|
|
inline static XMFLOAT4X4 glyph_transform(font const& font, int x, int y)
|
|
{
|
|
XMMATRIX transform =
|
|
XMMatrixScaling(font.desc->glyph_width, font.desc->glyph_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;
|
|
}
|
|
|
|
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);
|
|
|
|
glUniform1i(location.uniform.texture_sampler, 0);
|
|
glUniform2fv(location.uniform.cell, 1, (float *)&font.cell);
|
|
|
|
glBindVertexArray(vertex_array_object);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer);
|
|
}
|
|
|
|
void draw_string(font const& font, char const * const s, int x, int y)
|
|
{
|
|
int i = 0;
|
|
while (s[i] != 0) {
|
|
char c = s[i++];
|
|
if (!(c <= 0x20 || c > 0x7f)) {
|
|
XMFLOAT4X4 transform = glyph_transform(font, x, y);
|
|
glUniformMatrix4fv(location.uniform.transform, 1, GL_FALSE, (float *)&transform);
|
|
XMFLOAT2 glyph = glyph_coordinate(font, c);
|
|
glUniform2fv(location.uniform.glyph, 1, (float *)&glyph);
|
|
|
|
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, (void *)0);
|
|
}
|
|
|
|
x += font.desc->glyph_width;
|
|
}
|
|
}
|
|
}
|