2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 #include "cl_dyntexture.h"
28 dp_font_t dp_fonts[MAX_FONTS] = {{0}};
30 cvar_t r_textshadow = {CVAR_SAVE, "r_textshadow", "0", "draws a shadow on all text to improve readability (note: value controls offset, 1 = 1 pixel, 1.5 = 1.5 pixels, etc)"};
31 cvar_t r_textbrightness = {CVAR_SAVE, "r_textbrightness", "0", "additional brightness for text color codes (0 keeps colors as is, 1 makes them all white)"};
32 cvar_t r_textcontrast = {CVAR_SAVE, "r_textcontrast", "1", "additional contrast for text color codes (1 keeps colors as is, 0 makes them all black)"};
34 extern cvar_t v_glslgamma;
36 //=============================================================================
37 /* Support Routines */
39 #define FONT_FILESIZE 13468
40 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
41 static cachepic_t cachepics[MAX_CACHED_PICS];
42 static int numcachepics;
44 static rtexturepool_t *drawtexturepool;
46 static const unsigned char concharimage[FONT_FILESIZE] =
51 static rtexture_t *draw_generateconchars(void)
54 unsigned char buffer[65536][4], *data = NULL;
57 data = LoadTGA_BGRA (concharimage, FONT_FILESIZE);
59 for (i = 0;i < 8192;i++)
61 random = lhrandom (0.0,1.0);
62 buffer[i][2] = 83 + (unsigned char)(random * 64);
63 buffer[i][1] = 71 + (unsigned char)(random * 32);
64 buffer[i][0] = 23 + (unsigned char)(random * 16);
65 buffer[i][3] = data[i*4+0];
68 for (i = 8192;i < 32768;i++)
70 random = lhrandom (0.0,1.0);
71 buffer[i][2] = 95 + (unsigned char)(random * 64);
72 buffer[i][1] = 95 + (unsigned char)(random * 64);
73 buffer[i][0] = 95 + (unsigned char)(random * 64);
74 buffer[i][3] = data[i*4+0];
77 for (i = 32768;i < 40960;i++)
79 random = lhrandom (0.0,1.0);
80 buffer[i][2] = 83 + (unsigned char)(random * 64);
81 buffer[i][1] = 71 + (unsigned char)(random * 32);
82 buffer[i][0] = 23 + (unsigned char)(random * 16);
83 buffer[i][3] = data[i*4+0];
86 for (i = 40960;i < 65536;i++)
88 random = lhrandom (0.0,1.0);
89 buffer[i][2] = 96 + (unsigned char)(random * 64);
90 buffer[i][1] = 43 + (unsigned char)(random * 32);
91 buffer[i][0] = 27 + (unsigned char)(random * 32);
92 buffer[i][3] = data[i*4+0];
96 Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, &buffer[0][0]);
100 return R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, &buffer[0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
103 static rtexture_t *draw_generateditherpattern(void)
106 unsigned char pixels[8][8];
107 for (y = 0;y < 8;y++)
108 for (x = 0;x < 8;x++)
109 pixels[y][x] = ((x^y) & 4) ? 254 : 0;
110 return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST | TEXF_PRECACHE, palette_bgra_transparent);
113 typedef struct embeddedpic_s
122 static const embeddedpic_t embeddedpics[] =
125 "gfx/prydoncursor001", 16, 16,
144 "ui/mousepointer", 16, 16,
163 "gfx/crosshair1", 16, 16,
182 "gfx/crosshair2", 16, 16,
201 "gfx/crosshair3", 16, 16,
220 "gfx/crosshair4", 16, 16,
239 "gfx/crosshair5", 8, 8,
250 "gfx/crosshair6", 2, 2,
255 "gfx/crosshair7", 16, 16,
276 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
278 const embeddedpic_t *p;
279 for (p = embeddedpics;p->name;p++)
280 if (!strcmp(name, p->name))
281 return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA | TEXF_PRECACHE, palette_bgra_embeddedpic);
282 if (!strcmp(name, "gfx/conchars"))
283 return draw_generateconchars();
284 if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
285 return draw_generateditherpattern();
287 Con_Printf("Draw_CachePic: failed to load %s\n", name);
288 return r_texture_notexture;
297 // FIXME: move this to client somehow
298 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
304 unsigned char *lmpdata;
305 char lmpname[MAX_QPATH];
307 // check whether the picture has already been cached
308 crc = CRC_Block((unsigned char *)path, strlen(path));
309 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
310 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
311 if (!strcmp (path, pic->name))
314 if (numcachepics == MAX_CACHED_PICS)
316 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
317 // FIXME: support NULL in callers?
318 return cachepics; // return the first one
320 pic = cachepics + (numcachepics++);
321 strlcpy (pic->name, path, sizeof(pic->name));
323 pic->chain = cachepichash[hashkey];
324 cachepichash[hashkey] = pic;
326 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
327 pic->tex = CL_GetDynTexture( path );
328 // if so, set the width/height, too
330 pic->width = R_TextureWidth(pic->tex);
331 pic->height = R_TextureHeight(pic->tex);
332 // we're done now (early-out)
337 if (!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
338 flags |= TEXF_PRECACHE;
339 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
341 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer)
342 flags |= TEXF_COMPRESS;
344 // load a high quality image from disk if possible
345 pic->tex = loadtextureimage(drawtexturepool, path, false, flags, true);
346 if (pic->tex == NULL && !strncmp(path, "gfx/", 4))
348 // compatibility with older versions which did not require gfx/ prefix
349 pic->tex = loadtextureimage(drawtexturepool, path + 4, false, flags, true);
351 // if a high quality image was loaded, set the pic's size to match it, just
352 // in case there's no low quality version to get the size from
355 pic->width = R_TextureWidth(pic->tex);
356 pic->height = R_TextureHeight(pic->tex);
359 // now read the low quality version (wad or lmp file), and take the pic
360 // size from that even if we don't upload the texture, this way the pics
361 // show up the right size in the menu even if they were replaced with
362 // higher or lower resolution versions
363 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
364 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
366 if (developer_loading.integer)
367 Con_Printf("loading lump \"%s\"\n", path);
371 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
372 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
373 // if no high quality replacement image was found, upload the original low quality texture
375 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
379 else if ((lmpdata = W_GetLumpName (path + 4)))
381 if (developer_loading.integer)
382 Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
384 if (!strcmp(path, "gfx/conchars"))
386 // conchars is a raw image and with color 0 as transparent instead of 255
389 // if no high quality replacement image was found, upload the original low quality texture
391 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_font);
395 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
396 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
397 // if no high quality replacement image was found, upload the original low quality texture
399 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
403 // if it's not found on disk, generate an image
404 if (pic->tex == NULL)
406 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
407 pic->width = R_TextureWidth(pic->tex);
408 pic->height = R_TextureHeight(pic->tex);
414 cachepic_t *Draw_CachePic (const char *path)
416 return Draw_CachePic_Flags (path, 0);
419 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
424 crc = CRC_Block((unsigned char *)picname, strlen(picname));
425 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
426 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
427 if (!strcmp (picname, pic->name))
432 if (pic->tex && pic->width == width && pic->height == height)
434 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
442 if (numcachepics == MAX_CACHED_PICS)
444 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
445 // FIXME: support NULL in callers?
446 return cachepics; // return the first one
448 pic = cachepics + (numcachepics++);
449 strlcpy (pic->name, picname, sizeof(pic->name));
451 pic->chain = cachepichash[hashkey];
452 cachepichash[hashkey] = pic;
457 pic->height = height;
459 R_FreeTexture(pic->tex);
460 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, alpha ? TEXF_ALPHA : 0, NULL);
464 void Draw_FreePic(const char *picname)
469 // this doesn't really free the pic, but does free it's texture
470 crc = CRC_Block((unsigned char *)picname, strlen(picname));
471 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
472 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
474 if (!strcmp (picname, pic->name) && pic->tex)
476 R_FreeTexture(pic->tex);
484 extern int con_linewidth; // to force rewrapping
485 static void LoadFont(qboolean override, const char *name, dp_font_t *fnt)
488 float maxwidth, scale;
489 char widthfile[MAX_QPATH];
491 fs_offset_t widthbufsize;
493 if(override || !fnt->texpath[0])
494 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
496 if(drawtexturepool == NULL)
497 return; // before gl_draw_start, so will be loaded later
499 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
500 if(fnt->tex == r_texture_notexture)
502 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
503 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
506 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
508 // unspecified width == 1 (base width)
509 for(i = 1; i < 256; ++i)
510 fnt->width_of[i] = 1;
513 // FIXME load "name.width", if it fails, fill all with 1
514 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
516 float extraspacing = 0;
517 const char *p = widthbuf;
522 if(!COM_ParseToken_Simple(&p, false, false))
540 fnt->width_of[ch++] = atof(com_token) + extraspacing;
543 if(!strcmp(com_token, "extraspacing"))
545 if(!COM_ParseToken_Simple(&p, false, false))
547 extraspacing = atof(com_token);
549 else if(!strcmp(com_token, "scale"))
551 if(!COM_ParseToken_Simple(&p, false, false))
553 scale = atof(com_token);
557 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
558 if(!COM_ParseToken_Simple(&p, false, false))
568 maxwidth = fnt->width_of[1];
569 for(i = 2; i < 256; ++i)
570 maxwidth = max(maxwidth, fnt->width_of[i]);
571 fnt->maxwidth = maxwidth;
573 // fix up maxwidth for overlap
574 fnt->maxwidth *= scale;
577 if(fnt == FONT_CONSOLE)
578 con_linewidth = -1; // rewrap console in next frame
581 static dp_font_t *FindFont(const char *title)
584 for(i = 0; i < MAX_FONTS; ++i)
585 if(!strcmp(dp_fonts[i].title, title))
590 static void LoadFont_f(void)
596 Con_Printf("Available font commands:\n");
597 for(i = 0; i < MAX_FONTS; ++i)
598 Con_Printf(" loadfont %s gfx/tgafile\n", dp_fonts[i].title);
601 f = FindFont(Cmd_Argv(1));
604 Con_Printf("font function not found\n");
607 LoadFont(true, (Cmd_Argc() < 3) ? "gfx/conchars" : Cmd_Argv(2), f);
615 static void gl_draw_start(void)
618 drawtexturepool = R_AllocTexturePool();
621 memset(cachepichash, 0, sizeof(cachepichash));
623 for(i = 0; i < MAX_FONTS; ++i)
624 LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i]);
626 // draw the loading screen so people have something to see in the newly opened window
627 SCR_UpdateLoadingScreen(true);
630 static void gl_draw_shutdown(void)
632 R_FreeTexturePool(&drawtexturepool);
635 memset(cachepichash, 0, sizeof(cachepichash));
638 static void gl_draw_newmap(void)
642 void GL_Draw_Init (void)
645 Cvar_RegisterVariable(&r_textshadow);
646 Cvar_RegisterVariable(&r_textbrightness);
647 Cvar_RegisterVariable(&r_textcontrast);
648 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
649 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
651 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
652 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
653 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
654 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
655 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
656 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
657 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
658 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
659 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
660 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
661 if(!FONT_USER[i].title[0])
662 dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++);
665 void _DrawQ_Setup(void)
667 r_viewport_t viewport;
668 if (r_refdef.draw2dstage)
670 r_refdef.draw2dstage = true;
672 R_Viewport_InitOrtho(&viewport, &identitymatrix, r_refdef.view.x, vid.height - r_refdef.view.y - r_refdef.view.height, r_refdef.view.width, r_refdef.view.height, 0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100, NULL);
673 R_SetViewport(&viewport);
674 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
675 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
676 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
677 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
678 R_Mesh_Matrix(&identitymatrix);
682 GL_PolygonOffset(0, 0);
686 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
688 R_SetupGenericShader(true);
691 static void _DrawQ_ProcessDrawFlag(int flags)
695 if(flags == DRAWFLAG_ADDITIVE)
696 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
697 else if(flags == DRAWFLAG_MODULATE)
698 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
699 else if(flags == DRAWFLAG_2XMODULATE)
700 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
701 else if(flags == DRAWFLAG_SCREEN)
702 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
704 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
707 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
711 _DrawQ_ProcessDrawFlag(flags);
712 GL_Color(red, green, blue, alpha);
714 R_Mesh_VertexPointer(floats, 0, 0);
715 R_Mesh_ColorPointer(NULL, 0, 0);
716 R_Mesh_ResetTextureState();
717 R_SetupGenericShader(pic != NULL);
723 height = pic->height;
724 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
725 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
728 floats[12] = 0.0f;floats[13] = 0.0f;
729 floats[14] = 1.0f;floats[15] = 0.0f;
730 floats[16] = 1.0f;floats[17] = 1.0f;
731 floats[18] = 0.0f;floats[19] = 1.0f;
733 // AK07: lets be texel correct on the corners
735 float horz_offset = 0.5f / pic->width;
736 float vert_offset = 0.5f / pic->height;
738 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
739 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
740 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
741 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
746 floats[2] = floats[5] = floats[8] = floats[11] = 0;
747 floats[0] = floats[9] = x;
748 floats[1] = floats[4] = y;
749 floats[3] = floats[6] = x + width;
750 floats[7] = floats[10] = y + height;
752 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
755 void DrawQ_RotPic(float x, float y, cachepic_t *pic, float width, float height, float org_x, float org_y, float angle, float red, float green, float blue, float alpha, int flags)
758 float af = DEG2RAD(-angle); // forward
759 float ar = DEG2RAD(-angle + 90); // right
760 float sinaf = sin(af);
761 float cosaf = cos(af);
762 float sinar = sin(ar);
763 float cosar = cos(ar);
765 _DrawQ_ProcessDrawFlag(flags);
766 GL_Color(red, green, blue, alpha);
768 R_Mesh_VertexPointer(floats, 0, 0);
769 R_Mesh_ColorPointer(NULL, 0, 0);
770 R_Mesh_ResetTextureState();
771 R_SetupGenericShader(pic != NULL);
777 height = pic->height;
778 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
779 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
781 floats[12] = 0.0f;floats[13] = 0.0f;
782 floats[14] = 1.0f;floats[15] = 0.0f;
783 floats[16] = 1.0f;floats[17] = 1.0f;
784 floats[18] = 0.0f;floats[19] = 1.0f;
787 floats[2] = floats[5] = floats[8] = floats[11] = 0;
790 floats[0] = x - cosaf*org_x - cosar*org_y;
791 floats[1] = y - sinaf*org_x - sinar*org_y;
794 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
795 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
798 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
799 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
802 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
803 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
805 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
808 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
812 _DrawQ_ProcessDrawFlag(flags);
813 GL_Color(red, green, blue, alpha);
815 R_Mesh_VertexPointer(floats, 0, 0);
816 R_Mesh_ColorPointer(NULL, 0, 0);
817 R_Mesh_ResetTextureState();
818 R_SetupGenericShader(false);
820 floats[2] = floats[5] = floats[8] = floats[11] = 0;
821 floats[0] = floats[9] = x;
822 floats[1] = floats[4] = y;
823 floats[3] = floats[6] = x + width;
824 floats[7] = floats[10] = y + height;
826 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
829 /// color tag printing
830 static const vec4_t string_colors[] =
833 // LordHavoc: why on earth is cyan before magenta in Quake3?
834 // LordHavoc: note: Doom3 uses white for [0] and [7]
835 {0.0, 0.0, 0.0, 1.0}, // black
836 {1.0, 0.0, 0.0, 1.0}, // red
837 {0.0, 1.0, 0.0, 1.0}, // green
838 {1.0, 1.0, 0.0, 1.0}, // yellow
839 {0.0, 0.0, 1.0, 1.0}, // blue
840 {0.0, 1.0, 1.0, 1.0}, // cyan
841 {1.0, 0.0, 1.0, 1.0}, // magenta
842 {1.0, 1.0, 1.0, 1.0}, // white
843 // [515]'s BX_COLOREDTEXT extension
844 {1.0, 1.0, 1.0, 0.5}, // half transparent
845 {0.5, 0.5, 0.5, 1.0} // half brightness
846 // Black's color table
847 //{1.0, 1.0, 1.0, 1.0},
848 //{1.0, 0.0, 0.0, 1.0},
849 //{0.0, 1.0, 0.0, 1.0},
850 //{0.0, 0.0, 1.0, 1.0},
851 //{1.0, 1.0, 0.0, 1.0},
852 //{0.0, 1.0, 1.0, 1.0},
853 //{1.0, 0.0, 1.0, 1.0},
854 //{0.1, 0.1, 0.1, 1.0}
857 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
859 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
861 float C = r_textcontrast.value;
862 float B = r_textbrightness.value;
863 if (colorindex & 0x10000) // that bit means RGB color
865 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
866 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
867 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
868 color[3] = (colorindex & 0xf) / 15.0;
871 Vector4Copy(string_colors[colorindex], color);
872 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
875 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
876 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
880 float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
882 int num, colorindex = STRING_COLOR_DEFAULT;
891 if (!outcolor || *outcolor == -1)
892 colorindex = STRING_COLOR_DEFAULT;
894 colorindex = *outcolor;
896 maxwidth /= fnt->scale;
898 for (i = 0;i < *maxlen && text[i];i++)
902 if(x + fnt->width_of[(int) ' '] > maxwidth)
903 break; // oops, can't draw this
904 x += fnt->width_of[(int) ' '];
907 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen)
910 if (ch <= '9' && ch >= '0') // ^[0-9] found
912 colorindex = ch - '0';
915 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
917 // building colorindex...
918 ch = tolower(text[i+1]);
919 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
920 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
921 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
922 else tempcolorindex = 0;
925 ch = tolower(text[i+2]);
926 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
927 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
928 else tempcolorindex = 0;
931 ch = tolower(text[i+3]);
932 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
933 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
934 else tempcolorindex = 0;
937 colorindex = tempcolorindex | 0xf;
938 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
945 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
949 num = (unsigned char) text[i];
950 if(x + fnt->width_of[num] > maxwidth)
951 break; // oops, can't draw this
952 x += fnt->width_of[num];
958 *outcolor = colorindex;
960 return x * fnt->scale;
963 float DrawQ_String_Font(float startx, float starty, const char *text, size_t maxlen, float w, float h, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt)
965 int num, shadow, colorindex = STRING_COLOR_DEFAULT;
967 float x = startx, y, s, t, u, v, thisw;
971 float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
972 float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
973 float color4f[QUADELEMENTS_MAXQUADS*4*4];
978 tw = R_TextureWidth(fnt->tex);
979 th = R_TextureHeight(fnt->tex);
981 starty -= (fnt->scale - 1) * h * 0.5; // center
988 _DrawQ_ProcessDrawFlag(flags);
990 R_Mesh_ColorPointer(color4f, 0, 0);
991 R_Mesh_ResetTextureState();
992 R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
993 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
994 R_Mesh_VertexPointer(vertex3f, 0, 0);
995 R_SetupGenericShader(true);
1002 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1004 if (!outcolor || *outcolor == -1)
1005 colorindex = STRING_COLOR_DEFAULT;
1007 colorindex = *outcolor;
1009 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1015 x += r_textshadow.value;
1016 y += r_textshadow.value;
1018 for (i = 0;i < maxlen && text[i];i++)
1022 x += fnt->width_of[(int) ' '] * w;
1025 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen)
1028 if (ch <= '9' && ch >= '0') // ^[0-9] found
1030 colorindex = ch - '0';
1031 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1034 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1036 // building colorindex...
1037 ch = tolower(text[i+1]);
1038 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1039 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1040 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1041 else tempcolorindex = 0;
1044 ch = tolower(text[i+2]);
1045 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1046 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1047 else tempcolorindex = 0;
1050 ch = tolower(text[i+3]);
1051 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1052 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1053 else tempcolorindex = 0;
1056 colorindex = tempcolorindex | 0xf;
1057 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1058 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1059 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1066 else if (ch == STRING_COLOR_TAG)
1070 num = (unsigned char) text[i];
1071 thisw = fnt->width_of[num];
1072 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1073 s = (num & 15)*0.0625f + (0.5f / tw);
1074 t = (num >> 4)*0.0625f + (0.5f / th);
1075 u = 0.0625f * thisw - (1.0f / tw);
1076 v = 0.0625f - (1.0f / th);
1077 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1078 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1079 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1080 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1081 at[ 0] = s ; at[ 1] = t ;
1082 at[ 2] = s+u ; at[ 3] = t ;
1083 at[ 4] = s+u ; at[ 5] = t+v ;
1084 at[ 6] = s ; at[ 7] = t+v ;
1085 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1086 av[ 3] = x+w*thisw ; av[ 4] = y ; av[ 5] = 10;
1087 av[ 6] = x+w*thisw ; av[ 7] = y+h ; av[ 8] = 10;
1088 av[ 9] = x ; av[10] = y+h ; av[11] = 10;
1093 if (batchcount >= QUADELEMENTS_MAXQUADS)
1095 GL_LockArrays(0, batchcount * 4);
1096 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1097 GL_LockArrays(0, 0);
1108 GL_LockArrays(0, batchcount * 4);
1109 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1110 GL_LockArrays(0, 0);
1114 *outcolor = colorindex;
1116 // note: this relies on the proper text (not shadow) being drawn last
1120 float DrawQ_String(float startx, float starty, const char *text, size_t maxlen, float w, float h, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor, qboolean ignorecolorcodes)
1122 return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]);
1125 float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt)
1127 return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, ignorecolorcodes, fnt, 1000000000);
1130 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1132 return DrawQ_TextWidth_Font_UntilWidth_TrackColors(text, maxlen, NULL, ignorecolorcodes, fnt, maxWidth);
1137 // no ^xrgb management
1138 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1140 int color, numchars = 0;
1141 char *outputend2c = output2c + maxoutchars - 2;
1142 if (!outcolor || *outcolor == -1)
1143 color = STRING_COLOR_DEFAULT;
1147 maxreadchars = 1<<30;
1148 textend = text + maxreadchars;
1149 while (text != textend && *text)
1151 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1153 if (text[1] == STRING_COLOR_TAG)
1155 else if (text[1] >= '0' && text[1] <= '9')
1157 color = text[1] - '0';
1162 if (output2c >= outputend2c)
1164 *output2c++ = *text++;
1165 *output2c++ = color;
1168 output2c[0] = output2c[1] = 0;
1175 void DrawQ_SuperPic(float x, float y, cachepic_t *pic, float width, float height, float s1, float t1, float r1, float g1, float b1, float a1, float s2, float t2, float r2, float g2, float b2, float a2, float s3, float t3, float r3, float g3, float b3, float a3, float s4, float t4, float r4, float g4, float b4, float a4, int flags)
1179 _DrawQ_ProcessDrawFlag(flags);
1181 R_Mesh_VertexPointer(floats, 0, 0);
1182 R_Mesh_ColorPointer(floats + 20, 0, 0);
1183 R_Mesh_ResetTextureState();
1184 R_SetupGenericShader(pic != NULL);
1190 height = pic->height;
1191 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1192 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1193 floats[12] = s1;floats[13] = t1;
1194 floats[14] = s2;floats[15] = t2;
1195 floats[16] = s4;floats[17] = t4;
1196 floats[18] = s3;floats[19] = t3;
1199 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1200 floats[0] = floats[9] = x;
1201 floats[1] = floats[4] = y;
1202 floats[3] = floats[6] = x + width;
1203 floats[7] = floats[10] = y + height;
1204 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1205 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1206 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1207 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1209 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1212 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1214 _DrawQ_ProcessDrawFlag(flags);
1216 R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1217 R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1218 R_Mesh_ResetTextureState();
1219 R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
1220 R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1221 R_SetupGenericShader(mesh->texture != NULL);
1223 GL_LockArrays(0, mesh->num_vertices);
1224 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, mesh->data_element3s, 0, 0);
1225 GL_LockArrays(0, 0);
1228 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1232 _DrawQ_ProcessDrawFlag(flags);
1236 qglBegin(GL_LINE_LOOP);
1237 for (num = 0;num < mesh->num_vertices;num++)
1239 if (mesh->data_color4f)
1240 GL_Color(mesh->data_color4f[num*4+0], mesh->data_color4f[num*4+1], mesh->data_color4f[num*4+2], mesh->data_color4f[num*4+3]);
1241 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1247 //[515]: this is old, delete
1248 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1250 _DrawQ_ProcessDrawFlag(flags);
1252 R_SetupGenericShader(false);
1255 //qglLineWidth(width);CHECKGLERROR
1257 GL_Color(r,g,b,alpha);
1260 qglVertex2f(x1, y1);
1261 qglVertex2f(x2, y2);
1266 void DrawQ_SetClipArea(float x, float y, float width, float height)
1271 // We have to convert the con coords into real coords
1272 // OGL uses top to bottom
1273 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1274 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1275 iw = (int)(width * ((float)vid.width / vid_conwidth.integer));
1276 ih = (int)(height * ((float)vid.height / vid_conheight.integer));
1277 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1279 GL_ScissorTest(true);
1282 void DrawQ_ResetClipArea(void)
1285 GL_ScissorTest(false);
1288 void DrawQ_Finish(void)
1290 r_refdef.draw2dstage = false;
1293 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1294 void R_DrawGamma(void)
1297 if (!vid_usinghwgamma && !(r_glsl.integer && v_glslgamma.integer))
1299 // all the blends ignore depth
1300 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1301 R_Mesh_ColorPointer(NULL, 0, 0);
1302 R_Mesh_ResetTextureState();
1303 R_SetupGenericShader(false);
1305 GL_DepthRange(0, 1);
1306 GL_PolygonOffset(0, 0);
1307 GL_DepthTest(false);
1308 if (v_color_enable.integer)
1310 c[0] = v_color_white_r.value;
1311 c[1] = v_color_white_g.value;
1312 c[2] = v_color_white_b.value;
1315 c[0] = c[1] = c[2] = v_contrast.value;
1316 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1318 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1319 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1321 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1322 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
1323 VectorScale(c, 0.5, c);
1326 if (v_color_enable.integer)
1328 c[0] = v_color_black_r.value;
1329 c[1] = v_color_black_g.value;
1330 c[2] = v_color_black_b.value;
1333 c[0] = c[1] = c[2] = v_brightness.value;
1334 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1336 GL_BlendFunc(GL_ONE, GL_ONE);
1337 GL_Color(c[0], c[1], c[2], 1);
1338 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);