mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-29 23:52:22 +00:00
95f55dfc34
And add a function to process a passage into a set of views with glyphs. The views can be flowed: they have flow gravity and their sizes set to contain all the glyphs within each view (nominally, words). Nothing is tested yet, and font rendering is currently broken completely.
1034 lines
24 KiB
C
1034 lines
24 KiB
C
/*
|
|
glsl_draw.c
|
|
|
|
2D drawing support for GLSL
|
|
|
|
Copyright (C) 2011 Bill Currie <bill@taniwha.org>
|
|
|
|
Author: Bill Currie <bill@taniwha.org>
|
|
Date: 2011/12/23
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to:
|
|
|
|
Free Software Foundation, Inc.
|
|
59 Temple Place - Suite 330
|
|
Boston, MA 02111-1307, USA
|
|
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_STRING_H
|
|
# include <string.h>
|
|
#endif
|
|
#ifdef HAVE_STRINGS_H
|
|
# include <strings.h>
|
|
#endif
|
|
|
|
#include "QF/cvar.h"
|
|
#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"
|
|
#include "QF/ui/font.h"
|
|
#include "QF/ui/view.h"
|
|
|
|
#include "QF/GLSL/defines.h"
|
|
#include "QF/GLSL/funcs.h"
|
|
#include "QF/GLSL/qf_draw.h"
|
|
#include "QF/GLSL/qf_textures.h"
|
|
#include "QF/GLSL/qf_vid.h"
|
|
|
|
#include "r_internal.h"
|
|
|
|
typedef struct cachepic_s {
|
|
struct cachepic_s *next;
|
|
char *name;
|
|
qpic_t *pic;
|
|
} cachepic_t;
|
|
|
|
typedef struct {
|
|
float xyst[4];
|
|
float color[4];
|
|
} drawvert_t;
|
|
|
|
typedef struct glslfont_s {
|
|
font_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",
|
|
0
|
|
};
|
|
|
|
static const char *twod_frag_effects[] =
|
|
{
|
|
"QuakeForge.Fragment.palette",
|
|
"QuakeForge.Fragment.2d",
|
|
0
|
|
};
|
|
|
|
static const char *twod_alpha_frag_effects[] =
|
|
{
|
|
"QuakeForge.Fragment.2d.alpha",
|
|
0
|
|
};
|
|
|
|
static float proj_matrix[16];
|
|
|
|
static struct {
|
|
int program;
|
|
shaderparam_t texture;
|
|
shaderparam_t palette;
|
|
shaderparam_t matrix;
|
|
shaderparam_t vertex;
|
|
shaderparam_t color;
|
|
} quake_2d = {
|
|
.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;
|
|
static qpic_t *crosshair_pic;
|
|
static qpic_t *white_pic;
|
|
static qpic_t *backtile_pic;
|
|
static hashtab_t *pic_cache;
|
|
static int glsl_conback_texnum;
|
|
static int glsl_2d_scale = 1;
|
|
static cvar_t glsl_conback_texnum_cvar = {
|
|
.name = "glsl_conback_texnum",
|
|
.description =
|
|
"bind conback to this texture for debugging",
|
|
.default_value = "0",
|
|
.flags = CVAR_NONE,
|
|
.value = { .type = &cexpr_int, .value = &glsl_conback_texnum },
|
|
};
|
|
|
|
static qpic_t *
|
|
make_glpic (const char *name, qpic_t *p)
|
|
{
|
|
qpic_t *pic = 0;
|
|
|
|
if (p) {
|
|
pic = malloc (sizeof (qpic_t) + sizeof (subpic_t *));
|
|
pic->width = p->width;
|
|
pic->height = p->height;
|
|
subpic_t *sp = GLSL_ScrapSubpic (draw_scrap, pic->width, pic->height);
|
|
*(subpic_t **) pic->data = sp;
|
|
GLSL_SubpicUpdate (sp, p->data, 1);
|
|
}
|
|
return pic;
|
|
}
|
|
|
|
static void
|
|
pic_free (qpic_t *pic)
|
|
{
|
|
subpic_t *subpic = *(subpic_t **) pic->data;
|
|
|
|
GLSL_SubpicDelete (subpic);
|
|
free (pic);
|
|
}
|
|
|
|
//FIXME nicer allocator
|
|
static cachepic_t *
|
|
new_cachepic (const char *name, qpic_t *pic)
|
|
{
|
|
cachepic_t *cp;
|
|
|
|
cp = malloc (sizeof (cachepic_t));
|
|
cp->name = strdup (name);
|
|
cp->pic = pic;
|
|
return cp;
|
|
}
|
|
|
|
static const char *
|
|
cachepic_getkey (const void *_cp, void *unused)
|
|
{
|
|
return ((cachepic_t *) _cp)->name;
|
|
}
|
|
|
|
static void
|
|
cachepic_free (void *_cp, void *unused)
|
|
{
|
|
cachepic_t *cp = (cachepic_t *) _cp;
|
|
pic_free (cp->pic);
|
|
free (cp->name);
|
|
free (cp);
|
|
}
|
|
|
|
static qpic_t *
|
|
pic_data (const char *name, int w, int h, const byte *data)
|
|
{
|
|
qpic_t *pic;
|
|
qpic_t *glpic;
|
|
|
|
pic = malloc (field_offset (qpic_t, data[w * h]));
|
|
pic->width = w;
|
|
pic->height = h;
|
|
memcpy (pic->data, data, pic->width * pic->height);
|
|
glpic = make_glpic (name, pic);
|
|
free (pic);
|
|
return glpic;
|
|
}
|
|
|
|
static void
|
|
make_quad (qpic_t *pic, float x, float y, int w, int h,
|
|
int srcx, int srcy, int srcw, int srch, drawvert_t verts[6],
|
|
float *color)
|
|
{
|
|
subpic_t *sp = *(subpic_t **) pic->data;
|
|
float sl, sh, tl, th;
|
|
|
|
srcx += sp->rect->x;
|
|
srcy += sp->rect->y;
|
|
sl = (srcx) * sp->size;
|
|
sh = sl + (srcw) * sp->size;
|
|
tl = (srcy) * sp->size;
|
|
th = tl + (srch) * sp->size;
|
|
|
|
verts[0].xyst[0] = x;
|
|
verts[0].xyst[1] = y;
|
|
verts[0].xyst[2] = sl;
|
|
verts[0].xyst[3] = tl;
|
|
|
|
verts[1].xyst[0] = x + w;
|
|
verts[1].xyst[1] = y;
|
|
verts[1].xyst[2] = sh;
|
|
verts[1].xyst[3] = tl;
|
|
|
|
verts[2].xyst[0] = x + w;
|
|
verts[2].xyst[1] = y + h;
|
|
verts[2].xyst[2] = sh;
|
|
verts[2].xyst[3] = th;
|
|
|
|
verts[3].xyst[0] = x;
|
|
verts[3].xyst[1] = y;
|
|
verts[3].xyst[2] = sl;
|
|
verts[3].xyst[3] = tl;
|
|
|
|
verts[4].xyst[0] = x + w;
|
|
verts[4].xyst[1] = y + h;
|
|
verts[4].xyst[2] = sh;
|
|
verts[4].xyst[3] = th;
|
|
|
|
verts[5].xyst[0] = x;
|
|
verts[5].xyst[1] = y + h;
|
|
verts[5].xyst[2] = sl;
|
|
verts[5].xyst[3] = th;
|
|
|
|
QuatCopy (color, verts[0].color);
|
|
QuatCopy (color, verts[1].color);
|
|
QuatCopy (color, verts[2].color);
|
|
QuatCopy (color, verts[3].color);
|
|
QuatCopy (color, verts[4].color);
|
|
QuatCopy (color, verts[5].color);
|
|
}
|
|
|
|
static void
|
|
draw_pic (float x, float y, int w, int h, qpic_t *pic,
|
|
int srcx, int srcy, int srcw, int srch,
|
|
float *color)
|
|
{
|
|
drawvert_t verts[6];
|
|
void *v;
|
|
int size = sizeof (verts);
|
|
|
|
make_quad (pic, x, y, w, h, srcx, srcy, srcw, srch, verts, color);
|
|
draw_queue->size += size;
|
|
dstring_adjust (draw_queue);
|
|
v = draw_queue->str + draw_queue->size - size;
|
|
memcpy (v, verts, size);
|
|
}
|
|
|
|
qpic_t *
|
|
glsl_Draw_MakePic (int width, int height, const byte *data)
|
|
{
|
|
return pic_data (0, width, height, data);
|
|
}
|
|
|
|
void
|
|
glsl_Draw_DestroyPic (qpic_t *pic)
|
|
{
|
|
pic_free (pic);
|
|
}
|
|
|
|
qpic_t *
|
|
glsl_Draw_PicFromWad (const char *name)
|
|
{
|
|
return make_glpic (name, W_GetLumpName (name));
|
|
}
|
|
|
|
qpic_t *
|
|
glsl_Draw_CachePic (const char *path, qboolean alpha)
|
|
{
|
|
qpic_t *p, *pic;
|
|
cachepic_t *cpic;
|
|
|
|
if ((cpic = Hash_Find (pic_cache, path)))
|
|
return cpic->pic;
|
|
if (strlen (path) < 4 || strcmp (path + strlen (path) - 4, ".lmp")
|
|
|| !(p = (qpic_t *) QFS_LoadFile (QFS_FOpenFile (path), 0))) {
|
|
//FIXME load a null texture
|
|
Sys_Error ("Draw_CachePic: failed to load %s", path);
|
|
}
|
|
|
|
pic = make_glpic (path, p);
|
|
free (p);
|
|
cpic = new_cachepic (path, pic);
|
|
Hash_Add (pic_cache, cpic);
|
|
return pic;
|
|
}
|
|
|
|
void
|
|
glsl_Draw_UncachePic (const char *path)
|
|
{
|
|
Hash_Free (pic_cache, Hash_Del (pic_cache, path));
|
|
}
|
|
|
|
void
|
|
glsl_Draw_TextBox (int x, int y, int width, int lines, byte alpha)
|
|
{
|
|
static quat_t color = { 1, 1, 1, 0 };
|
|
qpic_t *p;
|
|
int cx, cy, n;
|
|
#define draw(px,py,pp) \
|
|
draw_pic (px, py, pp->width, pp->height, pp, \
|
|
0, 0, pp->width, pp->height, color)
|
|
|
|
color[3] = alpha / 255.0;
|
|
// draw left side
|
|
cx = x;
|
|
cy = y;
|
|
p = glsl_Draw_CachePic ("gfx/box_tl.lmp", true);
|
|
draw (cx, cy, p);
|
|
p = glsl_Draw_CachePic ("gfx/box_ml.lmp", true);
|
|
for (n = 0; n < lines; n++) {
|
|
cy += 8;
|
|
draw (cx, cy, p);
|
|
}
|
|
p = glsl_Draw_CachePic ("gfx/box_bl.lmp", true);
|
|
draw (cx, cy + 8, p);
|
|
|
|
// draw middle
|
|
cx += 8;
|
|
while (width > 0) {
|
|
cy = y;
|
|
p = glsl_Draw_CachePic ("gfx/box_tm.lmp", true);
|
|
draw (cx, cy, p);
|
|
p = glsl_Draw_CachePic ("gfx/box_mm.lmp", true);
|
|
for (n = 0; n < lines; n++) {
|
|
cy += 8;
|
|
if (n == 1)
|
|
p = glsl_Draw_CachePic ("gfx/box_mm2.lmp", true);
|
|
draw (cx, cy, p);
|
|
}
|
|
p = glsl_Draw_CachePic ("gfx/box_bm.lmp", true);
|
|
draw (cx, cy + 8, p);
|
|
width -= 2;
|
|
cx += 16;
|
|
}
|
|
|
|
// draw right side
|
|
cy = y;
|
|
p = glsl_Draw_CachePic ("gfx/box_tr.lmp", true);
|
|
draw (cx, cy, p);
|
|
p = glsl_Draw_CachePic ("gfx/box_mr.lmp", true);
|
|
for (n = 0; n < lines; n++) {
|
|
cy += 8;
|
|
draw (cx, cy, p);
|
|
}
|
|
p = glsl_Draw_CachePic ("gfx/box_br.lmp", true);
|
|
draw (cx, cy + 8, p);
|
|
#undef draw
|
|
}
|
|
|
|
static void
|
|
Draw_ClearCache (int phase, void *data)
|
|
{
|
|
if (phase)
|
|
return;
|
|
Hash_FlushTable (pic_cache);
|
|
}
|
|
|
|
void
|
|
glsl_Draw_Init (void)
|
|
{
|
|
shader_t *vert_shader, *frag_shader;
|
|
int i;
|
|
int frag, vert;
|
|
qpic_t *pic;
|
|
|
|
pic_cache = Hash_NewTable (127, cachepic_getkey, cachepic_free, 0, 0);
|
|
QFS_GamedirCallback (Draw_ClearCache, 0);
|
|
//FIXME temporary work around for the timing of cvar creation and palette
|
|
//loading
|
|
//crosshaircolor->callback (crosshaircolor);
|
|
|
|
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);
|
|
vert = GLSL_CompileShader ("quakeico.vert", vert_shader,
|
|
GL_VERTEX_SHADER);
|
|
frag = GLSL_CompileShader ("quake2d.frag", frag_shader,
|
|
GL_FRAGMENT_SHADER);
|
|
quake_2d.program = GLSL_LinkProgram ("quake2d", vert, frag);
|
|
GLSL_ResolveShaderParam (quake_2d.program, &quake_2d.texture);
|
|
GLSL_ResolveShaderParam (quake_2d.program, &quake_2d.palette);
|
|
GLSL_ResolveShaderParam (quake_2d.program, &quake_2d.matrix);
|
|
GLSL_ResolveShaderParam (quake_2d.program, &quake_2d.vertex);
|
|
GLSL_ResolveShaderParam (quake_2d.program, &quake_2d.color);
|
|
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");
|
|
if (draw_chars) {
|
|
for (i = 0; i < 256 * 64; i++) {
|
|
if (draw_chars[i] == 0) {
|
|
draw_chars[i] = 255; // proper transparent color
|
|
}
|
|
}
|
|
conchars = pic_data ("conchars", 128, 128, draw_chars);
|
|
} else {
|
|
qpic_t *charspic = Draw_Font8x8Pic ();
|
|
conchars = pic_data ("conchars", charspic->width,
|
|
charspic->height, charspic->data);
|
|
free (charspic);
|
|
}
|
|
|
|
pic = (qpic_t *) QFS_LoadFile (QFS_FOpenFile ("gfx/conback.lmp"), 0);
|
|
if (pic) {
|
|
SwapPic (pic);
|
|
conback_texture = GLSL_LoadQuakeTexture ("conback",
|
|
pic->width, pic->height,
|
|
pic->data);
|
|
free (pic);
|
|
}
|
|
|
|
pic = Draw_CrosshairPic ();
|
|
crosshair_pic = make_glpic ("crosshair", pic);
|
|
free (pic);
|
|
|
|
memset (white_block, 0xfe, sizeof (white_block));
|
|
white_pic = pic_data ("white_block", 8, 8, white_block);
|
|
|
|
backtile_pic = glsl_Draw_PicFromWad ("backtile");
|
|
if (!backtile_pic) {
|
|
backtile_pic = white_pic;
|
|
}
|
|
|
|
Cvar_Register (&glsl_conback_texnum_cvar, 0, 0);
|
|
}
|
|
|
|
static inline void
|
|
queue_character (int x, int y, byte chr)
|
|
{
|
|
quat_t color = {1, 1, 1, 1};
|
|
int cx, cy;
|
|
|
|
cx = chr % 16;
|
|
cy = chr / 16;
|
|
draw_pic (x, y, 8, 8, conchars, cx * 8, cy * 8, 8, 8, color);
|
|
}
|
|
|
|
static void
|
|
flush_2d (void)
|
|
{
|
|
GLSL_ScrapFlush (draw_scrap);
|
|
|
|
qfeglBindTexture (GL_TEXTURE_2D, GLSL_ScrapTexture (draw_scrap));
|
|
if (draw_queue->size) {
|
|
qfeglVertexAttribPointer (quake_2d.vertex.location, 4, GL_FLOAT,
|
|
0, 32, draw_queue->str);
|
|
qfeglVertexAttribPointer (quake_2d.color.location, 4, GL_FLOAT,
|
|
0, 32, draw_queue->str + 16);
|
|
|
|
qfeglDrawArrays (GL_TRIANGLES, 0, draw_queue->size / 32);
|
|
}
|
|
if (line_queue->size) {
|
|
qfeglVertexAttribPointer (quake_2d.vertex.location, 4, GL_FLOAT,
|
|
0, 32, line_queue->str);
|
|
qfeglVertexAttribPointer (quake_2d.color.location, 4, GL_FLOAT,
|
|
0, 32, line_queue->str + 16);
|
|
|
|
qfeglDrawArrays (GL_LINES, 0, line_queue->size / 32);
|
|
}
|
|
draw_queue->size = 0;
|
|
line_queue->size = 0;
|
|
}
|
|
|
|
void
|
|
glsl_Draw_CharBuffer (int x, int y, draw_charbuffer_t *buffer)
|
|
{
|
|
const byte *line = (byte *) buffer->chars;
|
|
int width = buffer->width;
|
|
int height = buffer->height;
|
|
while (height-- > 0) {
|
|
for (int i = 0; i < width; i++) {
|
|
glsl_Draw_Character (x + i * 8, y, line[i]);
|
|
}
|
|
line += width;
|
|
y += 8;
|
|
}
|
|
}
|
|
|
|
void
|
|
glsl_Draw_Character (int x, int y, unsigned int chr)
|
|
{
|
|
chr &= 255;
|
|
|
|
if (chr == 32)
|
|
return; // space
|
|
if (y <= -8)
|
|
return; // totally off screen
|
|
|
|
queue_character (x, y, chr);
|
|
}
|
|
|
|
void
|
|
glsl_Draw_String (int x, int y, const char *str)
|
|
{
|
|
byte chr;
|
|
|
|
if (!str || !str[0])
|
|
return; // totally off screen
|
|
if (y <= -8)
|
|
return; // totally off screen
|
|
|
|
while (*str) {
|
|
if ((chr = *str++) != 32)
|
|
queue_character (x, y, chr);
|
|
x += 8;
|
|
}
|
|
}
|
|
|
|
void
|
|
glsl_Draw_nString (int x, int y, const char *str, int count)
|
|
{
|
|
byte chr;
|
|
|
|
if (!str || !str[0])
|
|
return; // totally off screen
|
|
if (y <= -8)
|
|
return; // totally off screen
|
|
|
|
while (count-- && *str) {
|
|
if ((chr = *str++) != 32)
|
|
queue_character (x, y, chr);
|
|
x += 8;
|
|
}
|
|
}
|
|
|
|
void
|
|
glsl_Draw_AltString (int x, int y, const char *str)
|
|
{
|
|
byte chr;
|
|
|
|
if (!str || !str[0])
|
|
return;
|
|
if (y <= -8)
|
|
return; // totally off screen
|
|
|
|
while (*str) {
|
|
if ((chr = *str++ | 0x80) != (0x80 | 32)) { // Don't render spaces
|
|
queue_character (x, y, chr);
|
|
}
|
|
x += 8;
|
|
}
|
|
}
|
|
|
|
static void
|
|
draw_crosshair_plus (int ch, int x, int y)
|
|
{
|
|
glsl_Draw_Character (x - 4, y - 4, '+');
|
|
}
|
|
|
|
static void
|
|
draw_crosshair_pic (int ch, int x, int y)
|
|
{
|
|
static const int pos[CROSSHAIR_COUNT][4] = {
|
|
{0, 0, CROSSHAIR_WIDTH, CROSSHAIR_HEIGHT},
|
|
{CROSSHAIR_WIDTH, 0, CROSSHAIR_WIDTH, CROSSHAIR_HEIGHT},
|
|
{0, CROSSHAIR_HEIGHT, CROSSHAIR_WIDTH, CROSSHAIR_HEIGHT},
|
|
{CROSSHAIR_WIDTH, CROSSHAIR_HEIGHT, CROSSHAIR_WIDTH, CROSSHAIR_HEIGHT},
|
|
};
|
|
const int *p = pos[ch - 1];
|
|
|
|
draw_pic (x - CROSSHAIR_WIDTH / 2 + 1, y - CROSSHAIR_HEIGHT / 2 + 1,
|
|
CROSSHAIR_WIDTH, CROSSHAIR_HEIGHT, crosshair_pic,
|
|
p[0], p[1], p[2], p[3], crosshair_color);
|
|
}
|
|
|
|
static void (*crosshair_func[]) (int ch, int x, int y) = {
|
|
draw_crosshair_plus,
|
|
draw_crosshair_pic,
|
|
draw_crosshair_pic,
|
|
draw_crosshair_pic,
|
|
draw_crosshair_pic,
|
|
};
|
|
|
|
void
|
|
glsl_Draw_CrosshairAt (int ch, int x, int y)
|
|
{
|
|
unsigned c = ch - 1;
|
|
|
|
if (c >= sizeof (crosshair_func) / sizeof (crosshair_func[0]))
|
|
return;
|
|
|
|
crosshair_func[c] (c, x, y);
|
|
}
|
|
|
|
void
|
|
glsl_Draw_Crosshair (void)
|
|
{
|
|
int x, y;
|
|
int s = 2 * glsl_2d_scale;
|
|
|
|
x = vid.width / s + cl_crossx;
|
|
y = vid.height / s + cl_crossy;
|
|
|
|
glsl_Draw_CrosshairAt (crosshair, x, y);
|
|
}
|
|
|
|
void
|
|
glsl_Draw_Pic (int x, int y, qpic_t *pic)
|
|
{
|
|
static quat_t color = { 1, 1, 1, 1};
|
|
draw_pic (x, y, pic->width, pic->height, pic,
|
|
0, 0, pic->width, pic->height, color);
|
|
}
|
|
|
|
void
|
|
glsl_Draw_Picf (float x, float y, qpic_t *pic)
|
|
{
|
|
static quat_t color = { 1, 1, 1, 1};
|
|
draw_pic (x, y, pic->width, pic->height, pic,
|
|
0, 0, pic->width, pic->height, color);
|
|
}
|
|
|
|
void
|
|
glsl_Draw_SubPic (int x, int y, qpic_t *pic, int srcx, int srcy, int width,
|
|
int height)
|
|
{
|
|
static quat_t color = { 1, 1, 1, 1};
|
|
draw_pic (x, y, width, height, pic, srcx, srcy, width, height, color);
|
|
}
|
|
|
|
void
|
|
glsl_Draw_ConsoleBackground (int lines, byte alpha)
|
|
{
|
|
int s = glsl_2d_scale;
|
|
float ofs = (vid.height - s * lines) / (float) vid.height;
|
|
quat_t color = {1, 1, 1, bound (0, alpha, 255) / 255.0};
|
|
drawvert_t verts[] = {
|
|
{{ 0, 0, 0, ofs}},
|
|
{{vid.width / s, 0, 1, ofs}},
|
|
{{vid.width / s, lines, 1, 1}},
|
|
{{ 0, 0, 0, ofs}},
|
|
{{vid.width / s, lines, 1, 1}},
|
|
{{ 0, lines, 0, 1}},
|
|
};
|
|
|
|
GLSL_FlushText (); // Flush text that should be rendered before the console
|
|
|
|
QuatCopy (color, verts[0].color);
|
|
QuatCopy (color, verts[1].color);
|
|
QuatCopy (color, verts[2].color);
|
|
QuatCopy (color, verts[3].color);
|
|
QuatCopy (color, verts[4].color);
|
|
QuatCopy (color, verts[5].color);
|
|
|
|
if (glsl_conback_texnum)
|
|
qfeglBindTexture (GL_TEXTURE_2D, glsl_conback_texnum);
|
|
else
|
|
qfeglBindTexture (GL_TEXTURE_2D, conback_texture);
|
|
|
|
qfeglVertexAttribPointer (quake_2d.vertex.location, 4, GL_FLOAT,
|
|
0, sizeof (drawvert_t), &verts[0].xyst);
|
|
qfeglVertexAttribPointer (quake_2d.color.location, 4, GL_FLOAT,
|
|
0, sizeof (drawvert_t), &verts[0].color);
|
|
|
|
qfeglDrawArrays (GL_TRIANGLES, 0, 6);
|
|
}
|
|
|
|
void
|
|
glsl_Draw_TileClear (int x, int y, int w, int h)
|
|
{
|
|
static quat_t color = { 1, 1, 1, 1 };
|
|
vrect_t *tile_rect = VRect_New (x, y, w, h);
|
|
vrect_t *sub = VRect_New (0, 0, 0, 0); // filled in later;
|
|
subpic_t *sp = *(subpic_t **) backtile_pic->data;
|
|
int sub_sx, sub_sy, sub_ex, sub_ey;
|
|
int i, j;
|
|
|
|
sub_sx = x / sp->width;
|
|
sub_sy = y / sp->height;
|
|
sub_ex = (x + w + sp->width - 1) / sp->width;
|
|
sub_ey = (y + h + sp->height - 1) / sp->height;
|
|
for (j = sub_sy; j < sub_ey; j++) {
|
|
for (i = sub_sx; i < sub_ex; i++) {
|
|
vrect_t *t = sub;
|
|
|
|
sub->x = i * sp->width;
|
|
sub->y = j * sp->height;
|
|
sub->width = sp->width;
|
|
sub->height = sp->height;
|
|
sub = VRect_Intersect (sub, tile_rect);
|
|
VRect_Delete (t);
|
|
draw_pic (sub->x, sub->y, sub->width, sub->height, backtile_pic,
|
|
sub->x % sp->width, sub->y % sp->height,
|
|
sub->width, sub->height, color);
|
|
}
|
|
}
|
|
VRect_Delete (sub);
|
|
VRect_Delete (tile_rect);
|
|
flush_2d ();
|
|
}
|
|
|
|
void
|
|
glsl_Draw_Fill (int x, int y, int w, int h, int c)
|
|
{
|
|
quat_t color;
|
|
|
|
VectorScale (vid.palette + c * 3, 1.0f/255.0f, color);
|
|
color[3] = 1.0;
|
|
draw_pic (x, y, w, h, white_pic, 0, 0, 8, 8, color);
|
|
}
|
|
|
|
void
|
|
glsl_Draw_Line (int x0, int y0, int x1, int y1, int c)
|
|
{
|
|
subpic_t *sp = *(subpic_t **) white_pic->data;
|
|
float sl = sp->rect->x * sp->size;
|
|
float sh = sp->rect->x * sp->size;
|
|
float tl = sp->rect->y * sp->size;
|
|
float th = sp->rect->y * sp->size;
|
|
|
|
quat_t color = { VectorExpand (vid.palette + c * 3), 255 };
|
|
QuatScale (color, 1/255.0, color);
|
|
drawvert_t verts[2] = {
|
|
{ .xyst = { x0, y0, sl, tl }, .color = { QuatExpand (color) }, },
|
|
{ .xyst = { x1, y1, sh, th }, .color = { QuatExpand (color) }, },
|
|
};
|
|
|
|
void *v;
|
|
int size = sizeof (verts);
|
|
|
|
line_queue->size += size;
|
|
dstring_adjust (line_queue);
|
|
v = line_queue->str + line_queue->size - size;
|
|
memcpy (v, verts, size);
|
|
}
|
|
|
|
void
|
|
glsl_LineGraph (int x, int y, int *h_vals, int count, int height)
|
|
{
|
|
static int colors[] = { 0xd0, 0x4f, 0x6f };
|
|
|
|
while (count-- > 0) {
|
|
int h = *h_vals++;
|
|
int c = h < 9998 || h > 10000 ? 0xfe : colors[h - 9998];
|
|
h = min (h, height);
|
|
glsl_Draw_Line (x, y, x, y - h, c);
|
|
x++;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
draw_blendscreen (quat_t color)
|
|
{
|
|
draw_pic (0, 0, vid.width, vid.height, white_pic, 0, 0, 8, 8, color);
|
|
}
|
|
|
|
void
|
|
glsl_Draw_FadeScreen (void)
|
|
{
|
|
static quat_t color = { 0, 0, 0, 0.7 };
|
|
|
|
GLSL_FlushText (); // Flush text that should be rendered before the menu
|
|
draw_blendscreen (color);
|
|
}
|
|
|
|
static void
|
|
ortho_mat (float *proj, float xmin, float xmax, float ymin, float ymax,
|
|
float znear, float zfar)
|
|
{
|
|
proj[0] = 2 / (xmax - xmin);
|
|
proj[4] = 0;
|
|
proj[8] = 0;
|
|
proj[12] = -(xmax + xmin) / (xmax - xmin);
|
|
|
|
proj[1] = 0;
|
|
proj[5] = 2 / (ymax - ymin);
|
|
proj[9] = 0;
|
|
proj[13] = -(ymax + ymin) / (ymax - ymin);
|
|
|
|
proj[2] = 0;
|
|
proj[6] = 0;
|
|
proj[10] = -2 / (zfar - znear);
|
|
proj[14] = -(zfar + znear) / (zfar - znear);
|
|
|
|
proj[3] = 0;
|
|
proj[7] = 0;
|
|
proj[11] = 0;
|
|
proj[15] = 1;
|
|
}
|
|
|
|
static void
|
|
set_2d (int width, int height)
|
|
{
|
|
qfeglViewport (0, 0, vid.width, vid.height);
|
|
|
|
qfeglDisable (GL_DEPTH_TEST);
|
|
qfeglDisable (GL_CULL_FACE);
|
|
|
|
ortho_mat (proj_matrix, 0, width, height, 0, -99999, 99999);
|
|
|
|
qfeglUseProgram (quake_2d.program);
|
|
qfeglEnableVertexAttribArray (quake_2d.vertex.location);
|
|
qfeglEnableVertexAttribArray (quake_2d.color.location);
|
|
|
|
qfeglUniformMatrix4fv (quake_2d.matrix.location, 1, false, proj_matrix);
|
|
|
|
qfeglUniform1i (quake_2d.palette.location, 1);
|
|
qfeglActiveTexture (GL_TEXTURE0 + 1);
|
|
qfeglEnable (GL_TEXTURE_2D);
|
|
qfeglBindTexture (GL_TEXTURE_2D, glsl_palette);
|
|
|
|
qfeglUniform1i (quake_2d.texture.location, 0);
|
|
qfeglActiveTexture (GL_TEXTURE0 + 0);
|
|
qfeglEnable (GL_TEXTURE_2D);
|
|
qfeglBindTexture (GL_TEXTURE_2D, GLSL_ScrapTexture (draw_scrap));
|
|
}
|
|
|
|
void
|
|
GLSL_Set2D (void)
|
|
{
|
|
set_2d (vid.width, vid.height);
|
|
}
|
|
|
|
void
|
|
GLSL_Set2DScaled (void)
|
|
{
|
|
set_2d (vid.width / glsl_2d_scale, vid.height / glsl_2d_scale);
|
|
}
|
|
|
|
void
|
|
glsl_Draw_SetScale (int scale)
|
|
{
|
|
glsl_2d_scale = scale;
|
|
}
|
|
|
|
void
|
|
GLSL_End2D (void)
|
|
{
|
|
qfeglDisableVertexAttribArray (quake_2d.vertex.location);
|
|
qfeglDisableVertexAttribArray (quake_2d.color.location);
|
|
}
|
|
|
|
void
|
|
GLSL_DrawReset (void)
|
|
{
|
|
draw_queue->size = 0;
|
|
line_queue->size = 0;
|
|
}
|
|
|
|
void
|
|
GLSL_FlushText (void)
|
|
{
|
|
if (draw_queue->size || line_queue->size)
|
|
flush_2d ();
|
|
}
|
|
|
|
void
|
|
glsl_Draw_BlendScreen (quat_t color)
|
|
{
|
|
if (!color[3])
|
|
return;
|
|
draw_blendscreen (color);
|
|
}
|
|
|
|
int
|
|
glsl_Draw_AddFont (font_t *rfont)
|
|
{
|
|
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;
|
|
}
|
|
#if 0
|
|
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);
|
|
}
|
|
#endif
|
|
void
|
|
glsl_Draw_FontString (int x, int y, int fontid, const char *str)
|
|
{
|
|
#if 0
|
|
if (fontid < 0 || (unsigned) fontid > glsl_fonts.size) {
|
|
return;
|
|
}
|
|
glslfont_t *font = &glsl_fonts.a[fontid];
|
|
font_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);
|
|
#endif
|
|
}
|