[glsl] Implement font rendering

It's not the best code, but it does the job of getting the basics
working.
This commit is contained in:
Bill Currie 2022-12-06 01:18:01 +09:00
parent 1f5ec68b4a
commit 46967dbc05
4 changed files with 225 additions and 8 deletions

View file

@ -31,6 +31,7 @@
#include "QF/qtypes.h" #include "QF/qtypes.h"
typedef struct scrap_s scrap_t; typedef struct scrap_s scrap_t;
struct tex_s;
int GLSL_LoadQuakeTexture (const char *identifier, int width, int height, int GLSL_LoadQuakeTexture (const char *identifier, int width, int height,
const byte *data); const byte *data);
@ -40,6 +41,7 @@ int GLSL_LoadRGBTexture (const char *identifier, int width, int height,
const byte *data); const byte *data);
int GLSL_LoadRGBATexture (const char *identifier, int width, int height, int GLSL_LoadRGBATexture (const char *identifier, int width, int height,
const byte *data); const byte *data);
int GLSL_LoadTex (const char *identifier, int mips, struct tex_s *tex);
void GLSL_ReleaseTexture (int tex); void GLSL_ReleaseTexture (int tex);
void GLSL_TextureInit (void); void GLSL_TextureInit (void);

View file

@ -42,6 +42,7 @@
#include "QF/draw.h" #include "QF/draw.h"
#include "QF/dstring.h" #include "QF/dstring.h"
#include "QF/hash.h" #include "QF/hash.h"
#include "QF/image.h"
#include "QF/quakefs.h" #include "QF/quakefs.h"
#include "QF/sys.h" #include "QF/sys.h"
#include "QF/vid.h" #include "QF/vid.h"
@ -53,6 +54,8 @@
#include "QF/GLSL/qf_textures.h" #include "QF/GLSL/qf_textures.h"
#include "QF/GLSL/qf_vid.h" #include "QF/GLSL/qf_vid.h"
#include "r_font.h"
#include "r_text.h"
#include "r_internal.h" #include "r_internal.h"
typedef struct cachepic_s { typedef struct cachepic_s {
@ -66,6 +69,16 @@ typedef struct {
float color[4]; float color[4];
} drawvert_t; } drawvert_t;
typedef struct glslfont_s {
rfont_t *font;
GLuint texid;
} glslfont_t;
typedef struct glfontset_s
DARRAY_TYPE (glslfont_t) glslfontset_t;
static glslfontset_t glsl_fonts = DARRAY_STATIC_INIT (16);
static const char *twod_vert_effects[] = static const char *twod_vert_effects[] =
{ {
"QuakeForge.Vertex.2d", "QuakeForge.Vertex.2d",
@ -79,6 +92,12 @@ static const char *twod_frag_effects[] =
0 0
}; };
static const char *twod_alpha_frag_effects[] =
{
"QuakeForge.Fragment.2d.alpha",
0
};
static float proj_matrix[16]; static float proj_matrix[16];
static struct { static struct {
@ -89,17 +108,30 @@ static struct {
shaderparam_t vertex; shaderparam_t vertex;
shaderparam_t color; shaderparam_t color;
} quake_2d = { } quake_2d = {
0, .texture = {"texture", 1},
{"texture", 1}, .palette = {"palette", 1},
{"palette", 1}, .matrix = {"mvp_mat", 1},
{"mvp_mat", 1}, .vertex = {"vertex", 0},
{"vertex", 0}, .color = {"vcolor", 0},
{"vcolor", 0}, };
static struct {
int program;
shaderparam_t texture;
shaderparam_t matrix;
shaderparam_t vertex;
shaderparam_t color;
} alpha_2d = {
.texture = {"texture", 1},
.matrix = {"mvp_mat", 1},
.vertex = {"vertex", 0},
.color = {"vcolor", 0},
}; };
static scrap_t *draw_scrap; // hold all 2d images static scrap_t *draw_scrap; // hold all 2d images
static byte white_block[8 * 8]; static byte white_block[8 * 8];
static dstring_t *draw_queue; static dstring_t *draw_queue;
static dstring_t *glyph_queue;
static dstring_t *line_queue; static dstring_t *line_queue;
static qpic_t *conchars; static qpic_t *conchars;
static int conback_texture; static int conback_texture;
@ -380,6 +412,7 @@ glsl_Draw_Init (void)
draw_queue = dstring_new (); draw_queue = dstring_new ();
line_queue = dstring_new (); line_queue = dstring_new ();
glyph_queue = dstring_new ();
vert_shader = GLSL_BuildShader (twod_vert_effects); vert_shader = GLSL_BuildShader (twod_vert_effects);
frag_shader = GLSL_BuildShader (twod_frag_effects); frag_shader = GLSL_BuildShader (twod_frag_effects);
@ -396,6 +429,20 @@ glsl_Draw_Init (void)
GLSL_FreeShader (vert_shader); GLSL_FreeShader (vert_shader);
GLSL_FreeShader (frag_shader); GLSL_FreeShader (frag_shader);
vert_shader = GLSL_BuildShader (twod_vert_effects);
frag_shader = GLSL_BuildShader (twod_alpha_frag_effects);
vert = GLSL_CompileShader ("quakeico.vert", vert_shader,
GL_VERTEX_SHADER);
frag = GLSL_CompileShader ("alpha2d.frag", frag_shader,
GL_FRAGMENT_SHADER);
alpha_2d.program = GLSL_LinkProgram ("alpha2d", vert, frag);
GLSL_ResolveShaderParam (alpha_2d.program, &alpha_2d.texture);
GLSL_ResolveShaderParam (alpha_2d.program, &alpha_2d.matrix);
GLSL_ResolveShaderParam (alpha_2d.program, &alpha_2d.vertex);
GLSL_ResolveShaderParam (alpha_2d.program, &alpha_2d.color);
GLSL_FreeShader (vert_shader);
GLSL_FreeShader (frag_shader);
draw_scrap = GLSL_CreateScrap (2048, GL_LUMINANCE, 0); draw_scrap = GLSL_CreateScrap (2048, GL_LUMINANCE, 0);
draw_chars = W_GetLumpName ("conchars"); draw_chars = W_GetLumpName ("conchars");
@ -866,12 +913,121 @@ glsl_Draw_BlendScreen (quat_t color)
} }
int int
glsl_Draw_AddFont (struct rfont_s *font) glsl_Draw_AddFont (struct rfont_s *rfont)
{ {
return 0; int fontid = glsl_fonts.size;
DARRAY_OPEN_AT (&glsl_fonts, fontid, 1);
glslfont_t *font = &glsl_fonts.a[fontid];
font->font = rfont;
tex_t tex = {
.width = rfont->scrap.width,
.height = rfont->scrap.height,
.format = tex_a,
.loaded = 1,
.data = rfont->scrap_bitmap,
};
font->texid = GLSL_LoadTex ("", 1, &tex);
return fontid;
}
typedef struct {
vrect_t *glyph_rects;
dstring_t *batch;
int width;
int height;
float color[4];
} glslrgctx_t;
static void
glsl_render_glyph (uint32_t glyphid, int x, int y, void *_rgctx)
{
glslrgctx_t *rgctx = _rgctx;
vrect_t *rect = &rgctx->glyph_rects[glyphid];
dstring_t *batch = rgctx->batch;
int size = 6 * sizeof (drawvert_t);
batch->size += size;
dstring_adjust (batch);
drawvert_t *verts = (drawvert_t *) (batch->str + batch->size - size);
float w = rect->width;
float h = rect->height;
float u = rect->x;
float v = rect->y;
float s = 1.0 / rgctx->width;
float t = 1.0 / rgctx->height;
verts[0] = (drawvert_t) {
.xyst = { x, y, u * s, v * t },
};
verts[1] = (drawvert_t) {
.xyst = { x + w, y, (u + w) * s, v * t },
};
verts[2] = (drawvert_t) {
.xyst = { x + w, y + h, (u + w) * s, (v + h) * t },
};
verts[3] = (drawvert_t) {
.xyst = { x, y, u * s, v * t },
};
verts[4] = (drawvert_t) {
.xyst = { x + w, y + h, (u + w) * s, (v + h) * t },
};
verts[5] = (drawvert_t) {
.xyst = { x, y + h, u * s, (v + h) * t },
};
QuatCopy (rgctx->color, verts[0].color);
QuatCopy (rgctx->color, verts[1].color);
QuatCopy (rgctx->color, verts[2].color);
QuatCopy (rgctx->color, verts[3].color);
QuatCopy (rgctx->color, verts[4].color);
QuatCopy (rgctx->color, verts[5].color);
} }
void void
glsl_Draw_FontString (int x, int y, int fontid, const char *str) glsl_Draw_FontString (int x, int y, int fontid, const char *str)
{ {
if (fontid < 0 || (unsigned) fontid > glsl_fonts.size) {
return;
}
glslfont_t *font = &glsl_fonts.a[fontid];
rfont_t *rfont = font->font;
glslrgctx_t rgctx = {
.glyph_rects = rfont->glyph_rects,
.batch = glyph_queue,
.width = rfont->scrap.width,
.height = rfont->scrap.height,
};
quat_t color = { 127, 255, 153, 255 };
QuatScale (color, 1.0f/255.0f, rgctx.color);
//FIXME ewwwwwww
rtext_t text = {
.text = str,
.language = "en",
.script = HB_SCRIPT_LATIN,
.direction = HB_DIRECTION_LTR,
};
rshaper_t *shaper = RText_NewShaper (rfont);
RText_RenderText (shaper, &text, x, y, glsl_render_glyph, &rgctx);
RText_DeleteShaper (shaper);
qfeglUseProgram (alpha_2d.program);
qfeglEnableVertexAttribArray (alpha_2d.vertex.location);
qfeglEnableVertexAttribArray (alpha_2d.color.location);
qfeglUniformMatrix4fv (alpha_2d.matrix.location, 1, false, proj_matrix);
qfeglBindTexture (GL_TEXTURE_2D, font->texid);
qfeglBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
if (glyph_queue->size) {
qfeglVertexAttribPointer (alpha_2d.vertex.location, 4, GL_FLOAT,
0, 32, glyph_queue->str);
qfeglVertexAttribPointer (alpha_2d.color.location, 4, GL_FLOAT,
0, 32, glyph_queue->str + 16);
qfeglDrawArrays (GL_TRIANGLES, 0, glyph_queue->size / 32);
}
glyph_queue->size = 0;
qfeglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
qfeglUseProgram (quake_2d.program);
} }

View file

@ -39,6 +39,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "QF/cmd.h" #include "QF/cmd.h"
#include "QF/image.h"
#include "QF/mathlib.h" #include "QF/mathlib.h"
#include "QF/model.h" #include "QF/model.h"
#include "QF/render.h" #include "QF/render.h"
@ -238,6 +239,47 @@ GLSL_LoadRGBATexture (const char *identifier, int width, int height,
return tnum; return tnum;
} }
int
GLSL_LoadTex (const char *identifier, int linear, tex_t *tex)
{
GLuint tnum;
qfeglGenTextures (1, &tnum);
int format = GL_RGB;
switch (tex->format) {
case tex_l:
case tex_a:
format = GL_LUMINANCE;
break;
case tex_la:
format = GL_LUMINANCE_ALPHA;
break;
case tex_rgb:
format = GL_RGB;
break;
case tex_rgba:
format = GL_RGBA;
break;
default:
Sys_Error ("GL_CreateScrap: Invalid texture format");
}
qfeglBindTexture (GL_TEXTURE_2D, tnum);
qfeglTexImage2D (GL_TEXTURE_2D, 0, format, tex->width, tex->height,
0, format, GL_UNSIGNED_BYTE, tex->data);
qfeglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
qfeglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (linear) {
qfeglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
qfeglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} else {
qfeglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
qfeglTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
qfeglGenerateMipmap (GL_TEXTURE_2D);
return tnum;
}
void void
GLSL_ReleaseTexture (int tex) GLSL_ReleaseTexture (int tex)
{ {

View file

@ -432,6 +432,23 @@ main (void)
gl_FragColor = palettedColor (pix) * color; gl_FragColor = palettedColor (pix) * color;
} }
-- Fragment.2d.alpha
uniform sampler2D texture;
varying vec4 color;
varying vec2 st;
void
main (void)
{
float alpha;
alpha = texture2D (texture, st).r;
if (alpha == 0.0)
discard;
gl_FragColor = alpha * color;
}
-- Vertex.iqm -- Vertex.iqm
uniform mat4 mvp_mat; uniform mat4 mvp_mat;