From 46967dbc05504b4f85f1296fa02c0afb2bf8314f Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Tue, 6 Dec 2022 01:18:01 +0900 Subject: [PATCH] [glsl] Implement font rendering It's not the best code, but it does the job of getting the basics working. --- include/QF/GLSL/qf_textures.h | 2 + libs/video/renderer/glsl/glsl_draw.c | 172 +++++++++++++++++++++-- libs/video/renderer/glsl/glsl_textures.c | 42 ++++++ libs/video/renderer/glsl/quakeforge.glsl | 17 +++ 4 files changed, 225 insertions(+), 8 deletions(-) diff --git a/include/QF/GLSL/qf_textures.h b/include/QF/GLSL/qf_textures.h index 946d4d0c9..7e28acff3 100644 --- a/include/QF/GLSL/qf_textures.h +++ b/include/QF/GLSL/qf_textures.h @@ -31,6 +31,7 @@ #include "QF/qtypes.h" typedef struct scrap_s scrap_t; +struct tex_s; int GLSL_LoadQuakeTexture (const char *identifier, int width, int height, const byte *data); @@ -40,6 +41,7 @@ int GLSL_LoadRGBTexture (const char *identifier, int width, int height, const byte *data); int GLSL_LoadRGBATexture (const char *identifier, int width, int height, const byte *data); +int GLSL_LoadTex (const char *identifier, int mips, struct tex_s *tex); void GLSL_ReleaseTexture (int tex); void GLSL_TextureInit (void); diff --git a/libs/video/renderer/glsl/glsl_draw.c b/libs/video/renderer/glsl/glsl_draw.c index b08f97a15..1db1547cf 100644 --- a/libs/video/renderer/glsl/glsl_draw.c +++ b/libs/video/renderer/glsl/glsl_draw.c @@ -42,6 +42,7 @@ #include "QF/draw.h" #include "QF/dstring.h" #include "QF/hash.h" +#include "QF/image.h" #include "QF/quakefs.h" #include "QF/sys.h" #include "QF/vid.h" @@ -53,6 +54,8 @@ #include "QF/GLSL/qf_textures.h" #include "QF/GLSL/qf_vid.h" +#include "r_font.h" +#include "r_text.h" #include "r_internal.h" typedef struct cachepic_s { @@ -66,6 +69,16 @@ typedef struct { float color[4]; } 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[] = { "QuakeForge.Vertex.2d", @@ -79,6 +92,12 @@ static const char *twod_frag_effects[] = 0 }; +static const char *twod_alpha_frag_effects[] = +{ + "QuakeForge.Fragment.2d.alpha", + 0 +}; + static float proj_matrix[16]; static struct { @@ -89,17 +108,30 @@ static struct { shaderparam_t vertex; shaderparam_t color; } quake_2d = { - 0, - {"texture", 1}, - {"palette", 1}, - {"mvp_mat", 1}, - {"vertex", 0}, - {"vcolor", 0}, + .texture = {"texture", 1}, + .palette = {"palette", 1}, + .matrix = {"mvp_mat", 1}, + .vertex = {"vertex", 0}, + .color = {"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 byte white_block[8 * 8]; static dstring_t *draw_queue; +static dstring_t *glyph_queue; static dstring_t *line_queue; static qpic_t *conchars; static int conback_texture; @@ -380,6 +412,7 @@ glsl_Draw_Init (void) draw_queue = dstring_new (); line_queue = dstring_new (); + glyph_queue = dstring_new (); vert_shader = GLSL_BuildShader (twod_vert_effects); frag_shader = GLSL_BuildShader (twod_frag_effects); @@ -396,6 +429,20 @@ glsl_Draw_Init (void) GLSL_FreeShader (vert_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_chars = W_GetLumpName ("conchars"); @@ -866,12 +913,121 @@ glsl_Draw_BlendScreen (quat_t color) } 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 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); } diff --git a/libs/video/renderer/glsl/glsl_textures.c b/libs/video/renderer/glsl/glsl_textures.c index efebdc6e0..657ffa3f6 100644 --- a/libs/video/renderer/glsl/glsl_textures.c +++ b/libs/video/renderer/glsl/glsl_textures.c @@ -39,6 +39,7 @@ #include #include "QF/cmd.h" +#include "QF/image.h" #include "QF/mathlib.h" #include "QF/model.h" #include "QF/render.h" @@ -238,6 +239,47 @@ GLSL_LoadRGBATexture (const char *identifier, int width, int height, 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 GLSL_ReleaseTexture (int tex) { diff --git a/libs/video/renderer/glsl/quakeforge.glsl b/libs/video/renderer/glsl/quakeforge.glsl index 05001cb83..b6f16ca0e 100644 --- a/libs/video/renderer/glsl/quakeforge.glsl +++ b/libs/video/renderer/glsl/quakeforge.glsl @@ -432,6 +432,23 @@ main (void) 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 uniform mat4 mvp_mat;