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"
29 #include "ft2_fontdefs.h"
31 dp_font_t dp_fonts[MAX_FONTS] = {{0}};
33 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)"};
34 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)"};
35 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)"};
37 extern cvar_t v_glslgamma;
39 //=============================================================================
40 /* Support Routines */
42 #define FONT_FILESIZE 13468
43 #define MAX_CACHED_PICS 1024
44 #define CACHEPICHASHSIZE 256
45 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
46 static cachepic_t cachepics[MAX_CACHED_PICS];
47 static int numcachepics;
49 static rtexturepool_t *drawtexturepool;
51 static const unsigned char concharimage[FONT_FILESIZE] =
56 static rtexture_t *draw_generateconchars(void)
59 unsigned char buffer[65536][4], *data = NULL;
62 data = LoadTGA_BGRA (concharimage, FONT_FILESIZE);
64 for (i = 0;i < 8192;i++)
66 random = lhrandom (0.0,1.0);
67 buffer[i][2] = 83 + (unsigned char)(random * 64);
68 buffer[i][1] = 71 + (unsigned char)(random * 32);
69 buffer[i][0] = 23 + (unsigned char)(random * 16);
70 buffer[i][3] = data[i*4+0];
73 for (i = 8192;i < 32768;i++)
75 random = lhrandom (0.0,1.0);
76 buffer[i][2] = 95 + (unsigned char)(random * 64);
77 buffer[i][1] = 95 + (unsigned char)(random * 64);
78 buffer[i][0] = 95 + (unsigned char)(random * 64);
79 buffer[i][3] = data[i*4+0];
82 for (i = 32768;i < 40960;i++)
84 random = lhrandom (0.0,1.0);
85 buffer[i][2] = 83 + (unsigned char)(random * 64);
86 buffer[i][1] = 71 + (unsigned char)(random * 32);
87 buffer[i][0] = 23 + (unsigned char)(random * 16);
88 buffer[i][3] = data[i*4+0];
91 for (i = 40960;i < 65536;i++)
93 random = lhrandom (0.0,1.0);
94 buffer[i][2] = 96 + (unsigned char)(random * 64);
95 buffer[i][1] = 43 + (unsigned char)(random * 32);
96 buffer[i][0] = 27 + (unsigned char)(random * 32);
97 buffer[i][3] = data[i*4+0];
101 Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, &buffer[0][0]);
105 return R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, &buffer[0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
108 static rtexture_t *draw_generateditherpattern(void)
111 unsigned char pixels[8][8];
112 for (y = 0;y < 8;y++)
113 for (x = 0;x < 8;x++)
114 pixels[y][x] = ((x^y) & 4) ? 254 : 0;
115 return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST | TEXF_PRECACHE, palette_bgra_transparent);
118 typedef struct embeddedpic_s
127 static const embeddedpic_t embeddedpics[] =
130 "gfx/prydoncursor001", 16, 16,
149 "ui/mousepointer", 16, 16,
168 "gfx/crosshair1", 16, 16,
187 "gfx/crosshair2", 16, 16,
206 "gfx/crosshair3", 16, 16,
225 "gfx/crosshair4", 16, 16,
244 "gfx/crosshair5", 8, 8,
255 "gfx/crosshair6", 2, 2,
260 "gfx/crosshair7", 16, 16,
279 "gfx/editlights/cursor", 16, 16,
298 "gfx/editlights/light", 16, 16,
317 "gfx/editlights/noshadow", 16, 16,
336 "gfx/editlights/selection", 16, 16,
355 "gfx/editlights/cubemaplight", 16, 16,
374 "gfx/editlights/cubemapnoshadowlight", 16, 16,
395 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
397 const embeddedpic_t *p;
398 for (p = embeddedpics;p->name;p++)
399 if (!strcmp(name, p->name))
400 return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA | TEXF_PRECACHE, palette_bgra_embeddedpic);
401 if (!strcmp(name, "gfx/conchars"))
402 return draw_generateconchars();
403 if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
404 return draw_generateditherpattern();
406 Con_Printf("Draw_CachePic: failed to load %s\n", name);
407 return r_texture_notexture;
416 // FIXME: move this to client somehow
417 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
423 unsigned char *lmpdata;
424 char lmpname[MAX_QPATH];
426 // check whether the picture has already been cached
427 crc = CRC_Block((unsigned char *)path, strlen(path));
428 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
429 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
430 if (!strcmp (path, pic->name))
433 if (numcachepics == MAX_CACHED_PICS)
435 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
436 // FIXME: support NULL in callers?
437 return cachepics; // return the first one
439 pic = cachepics + (numcachepics++);
440 strlcpy (pic->name, path, sizeof(pic->name));
442 pic->chain = cachepichash[hashkey];
443 cachepichash[hashkey] = pic;
445 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
446 pic->tex = CL_GetDynTexture( path );
447 // if so, set the width/height, too
449 pic->width = R_TextureWidth(pic->tex);
450 pic->height = R_TextureHeight(pic->tex);
451 // we're done now (early-out)
456 if (!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
457 flags |= TEXF_PRECACHE;
458 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
460 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer)
461 flags |= TEXF_COMPRESS;
463 // load a high quality image from disk if possible
464 pic->tex = loadtextureimage(drawtexturepool, path, false, flags, true);
465 if (pic->tex == NULL && !strncmp(path, "gfx/", 4))
467 // compatibility with older versions which did not require gfx/ prefix
468 pic->tex = loadtextureimage(drawtexturepool, path + 4, false, flags, true);
470 // if a high quality image was loaded, set the pic's size to match it, just
471 // in case there's no low quality version to get the size from
474 pic->width = R_TextureWidth(pic->tex);
475 pic->height = R_TextureHeight(pic->tex);
478 // now read the low quality version (wad or lmp file), and take the pic
479 // size from that even if we don't upload the texture, this way the pics
480 // show up the right size in the menu even if they were replaced with
481 // higher or lower resolution versions
482 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
483 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
485 if (developer_loading.integer)
486 Con_Printf("loading lump \"%s\"\n", path);
490 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
491 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
492 // if no high quality replacement image was found, upload the original low quality texture
494 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
498 else if ((lmpdata = W_GetLumpName (path + 4)))
500 if (developer_loading.integer)
501 Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
503 if (!strcmp(path, "gfx/conchars"))
505 // conchars is a raw image and with color 0 as transparent instead of 255
508 // if no high quality replacement image was found, upload the original low quality texture
510 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_font);
514 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
515 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
516 // if no high quality replacement image was found, upload the original low quality texture
518 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
522 // if it's not found on disk, generate an image
523 if (pic->tex == NULL)
525 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
526 pic->width = R_TextureWidth(pic->tex);
527 pic->height = R_TextureHeight(pic->tex);
533 cachepic_t *Draw_CachePic (const char *path)
535 return Draw_CachePic_Flags (path, 0);
538 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
543 crc = CRC_Block((unsigned char *)picname, strlen(picname));
544 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
545 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
546 if (!strcmp (picname, pic->name))
551 if (pic->tex && pic->width == width && pic->height == height)
553 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
561 if (numcachepics == MAX_CACHED_PICS)
563 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
564 // FIXME: support NULL in callers?
565 return cachepics; // return the first one
567 pic = cachepics + (numcachepics++);
568 strlcpy (pic->name, picname, sizeof(pic->name));
570 pic->chain = cachepichash[hashkey];
571 cachepichash[hashkey] = pic;
576 pic->height = height;
578 R_FreeTexture(pic->tex);
579 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, alpha ? TEXF_ALPHA : 0, NULL);
583 void Draw_FreePic(const char *picname)
588 // this doesn't really free the pic, but does free it's texture
589 crc = CRC_Block((unsigned char *)picname, strlen(picname));
590 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
591 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
593 if (!strcmp (picname, pic->name) && pic->tex)
595 R_FreeTexture(pic->tex);
603 extern int con_linewidth; // to force rewrapping
604 static void LoadFont(qboolean override, const char *name, dp_font_t *fnt, int size)
607 float maxwidth, scale;
608 char widthfile[MAX_QPATH];
610 fs_offset_t widthbufsize;
612 if(override || !fnt->texpath[0])
613 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
615 if(drawtexturepool == NULL)
616 return; // before gl_draw_start, so will be loaded later
620 // clear freetype font
621 Font_UnloadFont(fnt->ft2);
625 fnt->ft2 = Font_Alloc();
628 if(!Font_LoadFont(fnt->texpath, size, fnt->ft2))
635 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
636 if(fnt->tex == r_texture_notexture)
638 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
639 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
642 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
644 // unspecified width == 1 (base width)
645 for(i = 1; i < 256; ++i)
646 fnt->width_of[i] = 1;
649 // FIXME load "name.width", if it fails, fill all with 1
650 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
652 float extraspacing = 0;
653 const char *p = widthbuf;
658 if(!COM_ParseToken_Simple(&p, false, false))
676 fnt->width_of[ch++] = atof(com_token) + extraspacing;
679 if(!strcmp(com_token, "extraspacing"))
681 if(!COM_ParseToken_Simple(&p, false, false))
683 extraspacing = atof(com_token);
685 else if(!strcmp(com_token, "scale"))
687 if(!COM_ParseToken_Simple(&p, false, false))
689 scale = atof(com_token);
693 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
694 if(!COM_ParseToken_Simple(&p, false, false))
704 maxwidth = fnt->width_of[1];
705 for(i = 2; i < 256; ++i)
706 maxwidth = max(maxwidth, fnt->width_of[i]);
707 fnt->maxwidth = maxwidth;
709 // fix up maxwidth for overlap
710 fnt->maxwidth *= scale;
713 if(fnt == FONT_CONSOLE)
714 con_linewidth = -1; // rewrap console in next frame
717 static dp_font_t *FindFont(const char *title)
720 for(i = 0; i < MAX_FONTS; ++i)
721 if(!strcmp(dp_fonts[i].title, title))
726 static void LoadFont_f(void)
732 Con_Printf("Available font commands:\n");
733 for(i = 0; i < MAX_FONTS; ++i)
734 Con_Printf(" loadfont %s gfx/tgafile [size]\n", dp_fonts[i].title);
737 f = FindFont(Cmd_Argv(1));
740 Con_Printf("font function not found\n");
744 size = atoi(Cmd_Argv(3));
747 LoadFont(true, (Cmd_Argc() < 3) ? "gfx/conchars" : Cmd_Argv(2), f, size);
755 static void gl_draw_start(void)
758 drawtexturepool = R_AllocTexturePool();
761 memset(cachepichash, 0, sizeof(cachepichash));
765 for(i = 0; i < MAX_FONTS; ++i)
766 LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i], 16);
768 // draw the loading screen so people have something to see in the newly opened window
769 SCR_UpdateLoadingScreen(true);
772 static void gl_draw_shutdown(void)
776 R_FreeTexturePool(&drawtexturepool);
779 memset(cachepichash, 0, sizeof(cachepichash));
782 static void gl_draw_newmap(void)
787 void GL_Draw_Init (void)
790 Cvar_RegisterVariable(&r_textshadow);
791 Cvar_RegisterVariable(&r_textbrightness);
792 Cvar_RegisterVariable(&r_textcontrast);
793 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
794 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
796 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
797 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
798 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
799 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
800 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
801 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
802 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
803 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
804 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
805 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
806 if(!FONT_USER[i].title[0])
807 dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++);
810 void _DrawQ_Setup(void)
812 if (r_refdef.draw2dstage)
814 r_refdef.draw2dstage = true;
816 qglViewport(r_refdef.view.x, vid.height - (r_refdef.view.y + r_refdef.view.height), r_refdef.view.width, r_refdef.view.height);CHECKGLERROR
817 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
818 GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
819 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
820 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
821 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
822 R_Mesh_Matrix(&identitymatrix);
826 GL_PolygonOffset(0, 0);
830 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
832 R_SetupGenericShader(true);
835 static void _DrawQ_ProcessDrawFlag(int flags)
839 if(flags == DRAWFLAG_ADDITIVE)
840 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
841 else if(flags == DRAWFLAG_MODULATE)
842 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
843 else if(flags == DRAWFLAG_2XMODULATE)
844 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
845 else if(flags == DRAWFLAG_SCREEN)
846 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
848 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
851 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
855 _DrawQ_ProcessDrawFlag(flags);
856 GL_Color(red, green, blue, alpha);
858 R_Mesh_VertexPointer(floats, 0, 0);
859 R_Mesh_ColorPointer(NULL, 0, 0);
860 R_Mesh_ResetTextureState();
861 R_SetupGenericShader(pic != NULL);
867 height = pic->height;
868 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
869 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
872 floats[12] = 0.0f;floats[13] = 0.0f;
873 floats[14] = 1.0f;floats[15] = 0.0f;
874 floats[16] = 1.0f;floats[17] = 1.0f;
875 floats[18] = 0.0f;floats[19] = 1.0f;
877 // AK07: lets be texel correct on the corners
879 float horz_offset = 0.5f / pic->width;
880 float vert_offset = 0.5f / pic->height;
882 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
883 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
884 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
885 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
890 floats[2] = floats[5] = floats[8] = floats[11] = 0;
891 floats[0] = floats[9] = x;
892 floats[1] = floats[4] = y;
893 floats[3] = floats[6] = x + width;
894 floats[7] = floats[10] = y + height;
896 R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
899 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)
902 float af = DEG2RAD(-angle); // forward
903 float ar = DEG2RAD(-angle + 90); // right
904 float sinaf = sin(af);
905 float cosaf = cos(af);
906 float sinar = sin(ar);
907 float cosar = cos(ar);
909 _DrawQ_ProcessDrawFlag(flags);
910 GL_Color(red, green, blue, alpha);
912 R_Mesh_VertexPointer(floats, 0, 0);
913 R_Mesh_ColorPointer(NULL, 0, 0);
914 R_Mesh_ResetTextureState();
915 R_SetupGenericShader(pic != NULL);
921 height = pic->height;
922 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
923 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
925 floats[12] = 0.0f;floats[13] = 0.0f;
926 floats[14] = 1.0f;floats[15] = 0.0f;
927 floats[16] = 1.0f;floats[17] = 1.0f;
928 floats[18] = 0.0f;floats[19] = 1.0f;
931 floats[2] = floats[5] = floats[8] = floats[11] = 0;
934 floats[0] = x - cosaf*org_x - cosar*org_y;
935 floats[1] = y - sinaf*org_x - sinar*org_y;
938 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
939 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
942 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
943 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
946 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
947 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
949 R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
952 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
956 _DrawQ_ProcessDrawFlag(flags);
957 GL_Color(red, green, blue, alpha);
959 R_Mesh_VertexPointer(floats, 0, 0);
960 R_Mesh_ColorPointer(NULL, 0, 0);
961 R_Mesh_ResetTextureState();
962 R_SetupGenericShader(false);
964 floats[2] = floats[5] = floats[8] = floats[11] = 0;
965 floats[0] = floats[9] = x;
966 floats[1] = floats[4] = y;
967 floats[3] = floats[6] = x + width;
968 floats[7] = floats[10] = y + height;
970 R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
973 /// color tag printing
974 static const vec4_t string_colors[] =
977 // LordHavoc: why on earth is cyan before magenta in Quake3?
978 // LordHavoc: note: Doom3 uses white for [0] and [7]
979 {0.0, 0.0, 0.0, 1.0}, // black
980 {1.0, 0.0, 0.0, 1.0}, // red
981 {0.0, 1.0, 0.0, 1.0}, // green
982 {1.0, 1.0, 0.0, 1.0}, // yellow
983 {0.0, 0.0, 1.0, 1.0}, // blue
984 {0.0, 1.0, 1.0, 1.0}, // cyan
985 {1.0, 0.0, 1.0, 1.0}, // magenta
986 {1.0, 1.0, 1.0, 1.0}, // white
987 // [515]'s BX_COLOREDTEXT extension
988 {1.0, 1.0, 1.0, 0.5}, // half transparent
989 {0.5, 0.5, 0.5, 1.0} // half brightness
990 // Black's color table
991 //{1.0, 1.0, 1.0, 1.0},
992 //{1.0, 0.0, 0.0, 1.0},
993 //{0.0, 1.0, 0.0, 1.0},
994 //{0.0, 0.0, 1.0, 1.0},
995 //{1.0, 1.0, 0.0, 1.0},
996 //{0.0, 1.0, 1.0, 1.0},
997 //{1.0, 0.0, 1.0, 1.0},
998 //{0.1, 0.1, 0.1, 1.0}
1001 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1003 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1005 float C = r_textcontrast.value;
1006 float B = r_textbrightness.value;
1007 if (colorindex & 0x10000) // that bit means RGB color
1009 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1010 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1011 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1012 color[3] = (colorindex & 0xf) / 15.0;
1015 Vector4Copy(string_colors[colorindex], color);
1016 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1019 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1020 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1024 float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
1026 int num, colorindex = STRING_COLOR_DEFAULT;
1035 if (!outcolor || *outcolor == -1)
1036 colorindex = STRING_COLOR_DEFAULT;
1038 colorindex = *outcolor;
1040 maxwidth /= fnt->scale;
1042 for (i = 0;i < *maxlen && text[i];i++)
1046 if(x + fnt->width_of[(int) ' '] > maxwidth)
1047 break; // oops, can't draw this
1048 x += fnt->width_of[(int) ' '];
1051 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen)
1054 if (ch <= '9' && ch >= '0') // ^[0-9] found
1056 colorindex = ch - '0';
1059 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1061 // building colorindex...
1062 ch = tolower(text[i+1]);
1063 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1064 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1065 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1066 else tempcolorindex = 0;
1069 ch = tolower(text[i+2]);
1070 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1071 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1072 else tempcolorindex = 0;
1075 ch = tolower(text[i+3]);
1076 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1077 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1078 else tempcolorindex = 0;
1081 colorindex = tempcolorindex | 0xf;
1082 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1089 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1093 num = (unsigned char) text[i];
1094 if(x + fnt->width_of[num] > maxwidth)
1095 break; // oops, can't draw this
1096 x += fnt->width_of[num];
1102 *outcolor = colorindex;
1104 return x * fnt->scale;
1107 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)
1109 int shadow, colorindex = STRING_COLOR_DEFAULT;
1111 float x = startx, y, s, t, u, v, thisw;
1112 float *av, *at, *ac;
1115 float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1116 float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1117 float color4f[QUADELEMENTS_MAXQUADS*4*4];
1119 Uchar prevch = 0; // used for kerning
1121 ft2_font_map_t *prevmap = NULL;
1122 ft2_font_map_t *map = NULL;
1123 float ftbase_x, ftbase_y;
1124 const char *text_start = text;
1128 tw = R_TextureWidth(fnt->tex);
1129 th = R_TextureHeight(fnt->tex);
1131 starty -= (fnt->scale - 1) * h * 0.5; // center
1135 // draw the font at its baseline when using freetype
1137 ftbase_y = h * (5.0/6.0);
1142 _DrawQ_ProcessDrawFlag(flags);
1144 R_Mesh_ColorPointer(color4f, 0, 0);
1145 R_Mesh_ResetTextureState();
1147 R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
1148 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
1149 R_Mesh_VertexPointer(vertex3f, 0, 0);
1150 R_SetupGenericShader(true);
1157 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1161 if (!outcolor || *outcolor == -1)
1162 colorindex = STRING_COLOR_DEFAULT;
1164 colorindex = *outcolor;
1166 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
1172 x += r_textshadow.value;
1173 y += r_textshadow.value;
1175 for (i = 0;i < maxlen && *text;)
1177 if (*text == ' ' && !fnt->ft2)
1179 x += fnt->width_of[(int) ' '] * w;
1184 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen)
1189 if (ch <= '9' && ch >= '0') // ^[0-9] found
1191 colorindex = ch - '0';
1192 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
1195 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1197 // building colorindex...
1198 ch = tolower(text[1]);
1199 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1200 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1201 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1202 else tempcolorindex = 0;
1205 ch = tolower(text[2]);
1206 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1207 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1208 else tempcolorindex = 0;
1211 ch = tolower(text[3]);
1212 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1213 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1214 else tempcolorindex = 0;
1217 colorindex = tempcolorindex | 0xf;
1218 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1219 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1220 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
1228 else if (ch == STRING_COLOR_TAG)
1236 ch = u8_getchar(text, &text);
1237 i = text - text_start;
1238 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1239 // this way we don't need to rebind fnt->tex for every old-style character
1240 #define oldstyle_map ((ft2_font_map_t*)-1)
1241 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1242 if (!fnt->ft2 || (ch >= 0xE000 && ch <= 0xE0FF))
1252 // switching from freetype to non-freetype rendering
1253 GL_LockArrays(0, batchcount * 4);
1254 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, NULL, quadelements, 0, 0);
1255 GL_LockArrays(0, 0);
1261 if (map != oldstyle_map)
1263 R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
1264 R_SetupGenericShader(true);
1269 //num = (unsigned char) text[i];
1270 //thisw = fnt->width_of[num];
1271 thisw = fnt->width_of[ch];
1272 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1273 s = (ch & 15)*0.0625f + (0.5f / tw);
1274 t = (ch >> 4)*0.0625f + (0.5f / th);
1275 u = 0.0625f * thisw - (1.0f / tw);
1276 v = 0.0625f - (1.0f / th);
1277 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1278 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1279 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1280 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1281 at[ 0] = s ; at[ 1] = t ;
1282 at[ 2] = s+u ; at[ 3] = t ;
1283 at[ 4] = s+u ; at[ 5] = t+v ;
1284 at[ 6] = s ; at[ 7] = t+v ;
1285 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1286 av[ 3] = x+w*thisw ; av[ 4] = y ; av[ 5] = 10;
1287 av[ 6] = x+w*thisw ; av[ 7] = y+h ; av[ 8] = 10;
1288 av[ 9] = x ; av[10] = y+h ; av[11] = 10;
1293 if (batchcount >= QUADELEMENTS_MAXQUADS)
1295 GL_LockArrays(0, batchcount * 4);
1296 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, NULL, quadelements, 0, 0);
1297 GL_LockArrays(0, 0);
1305 map = fnt->ft2->font_map;
1306 while(map && map->start + FONT_CHARS_PER_MAP < ch)
1310 if (!Font_LoadMapForIndex(fnt->ft2, ch, &map))
1317 // this shouldn't happen
1327 // we need a different character map, render what we currently have:
1328 GL_LockArrays(0, batchcount * 4);
1329 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, NULL, quadelements, 0, 0);
1330 GL_LockArrays(0, 0);
1336 R_Mesh_TexBind(0, R_GetTexture(map->texture));
1337 R_SetupGenericShader(true);
1341 if (developer.integer && ch > 255)
1344 if (u8_fromchar(ch, buf, sizeof(buf)) > 0)
1346 fprintf(stderr, "char %x: %s\n", (int)ch, buf);
1351 mapch = ch - map->start;
1352 thisw = map->glyphs[mapch].advance_x;
1356 if (!developer.integer && prevch && Font_GetKerning(fnt->ft2, prevch, ch, &kx, &ky))
1365 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1366 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1367 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1368 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1369 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1370 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1371 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1372 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1373 av[ 0] = x + w * map->glyphs[mapch].vxmin; av[ 1] = y + h * map->glyphs[mapch].vymin; av[ 2] = 10;
1374 av[ 3] = x + w * map->glyphs[mapch].vxmax; av[ 4] = y + h * map->glyphs[mapch].vymin; av[ 5] = 10;
1375 av[ 6] = x + w * map->glyphs[mapch].vxmax; av[ 7] = y + h * map->glyphs[mapch].vymax; av[ 8] = 10;
1376 av[ 9] = x + w * map->glyphs[mapch].vxmin; av[10] = y + h * map->glyphs[mapch].vymax; av[11] = 10;
1385 if (batchcount >= QUADELEMENTS_MAXQUADS)
1387 GL_LockArrays(0, batchcount * 4);
1388 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, NULL, quadelements, 0, 0);
1389 GL_LockArrays(0, 0);
1404 GL_LockArrays(0, batchcount * 4);
1405 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, NULL, quadelements, 0, 0);
1406 GL_LockArrays(0, 0);
1410 *outcolor = colorindex;
1412 // note: this relies on the proper text (not shadow) being drawn last
1416 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)
1418 return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]);
1421 float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt)
1423 return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, ignorecolorcodes, fnt, 1000000000);
1426 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1428 return DrawQ_TextWidth_Font_UntilWidth_TrackColors(text, maxlen, NULL, ignorecolorcodes, fnt, maxWidth);
1433 // no ^xrgb management
1434 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1436 int color, numchars = 0;
1437 char *outputend2c = output2c + maxoutchars - 2;
1438 if (!outcolor || *outcolor == -1)
1439 color = STRING_COLOR_DEFAULT;
1443 maxreadchars = 1<<30;
1444 textend = text + maxreadchars;
1445 while (text != textend && *text)
1447 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1449 if (text[1] == STRING_COLOR_TAG)
1451 else if (text[1] >= '0' && text[1] <= '9')
1453 color = text[1] - '0';
1458 if (output2c >= outputend2c)
1460 *output2c++ = *text++;
1461 *output2c++ = color;
1464 output2c[0] = output2c[1] = 0;
1471 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)
1475 _DrawQ_ProcessDrawFlag(flags);
1477 R_Mesh_VertexPointer(floats, 0, 0);
1478 R_Mesh_ColorPointer(floats + 20, 0, 0);
1479 R_Mesh_ResetTextureState();
1480 R_SetupGenericShader(pic != NULL);
1486 height = pic->height;
1487 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1488 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1489 floats[12] = s1;floats[13] = t1;
1490 floats[14] = s2;floats[15] = t2;
1491 floats[16] = s4;floats[17] = t4;
1492 floats[18] = s3;floats[19] = t3;
1495 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1496 floats[0] = floats[9] = x;
1497 floats[1] = floats[4] = y;
1498 floats[3] = floats[6] = x + width;
1499 floats[7] = floats[10] = y + height;
1500 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1501 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1502 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1503 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1505 R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
1508 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1510 _DrawQ_ProcessDrawFlag(flags);
1512 R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1513 R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1514 R_Mesh_ResetTextureState();
1515 R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
1516 R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1517 R_SetupGenericShader(mesh->texture != NULL);
1519 GL_LockArrays(0, mesh->num_vertices);
1520 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, NULL, mesh->data_element3s, 0, 0);
1521 GL_LockArrays(0, 0);
1524 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1528 _DrawQ_ProcessDrawFlag(flags);
1532 qglBegin(GL_LINE_LOOP);
1533 for (num = 0;num < mesh->num_vertices;num++)
1535 if (mesh->data_color4f)
1536 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]);
1537 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1543 //[515]: this is old, delete
1544 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1546 _DrawQ_ProcessDrawFlag(flags);
1548 R_SetupGenericShader(false);
1551 qglLineWidth(width);CHECKGLERROR
1553 GL_Color(r,g,b,alpha);
1556 qglVertex2f(x1, y1);
1557 qglVertex2f(x2, y2);
1562 void DrawQ_SetClipArea(float x, float y, float width, float height)
1566 // We have to convert the con coords into real coords
1567 // OGL uses top to bottom
1568 GL_Scissor((int)(0.5 + x * ((float)vid.width / vid_conwidth.integer)), (int)(0.5 + y * ((float) vid.height / vid_conheight.integer)), (int)(width * ((float)vid.width / vid_conwidth.integer)), (int)(height * ((float)vid.height / vid_conheight.integer)));
1570 GL_ScissorTest(true);
1573 void DrawQ_ResetClipArea(void)
1576 GL_ScissorTest(false);
1579 void DrawQ_Finish(void)
1581 r_refdef.draw2dstage = false;
1584 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1585 void R_DrawGamma(void)
1588 if (!vid_usinghwgamma && !(r_glsl.integer && v_glslgamma.integer))
1590 // all the blends ignore depth
1591 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1592 R_Mesh_ColorPointer(NULL, 0, 0);
1593 R_Mesh_ResetTextureState();
1594 R_SetupGenericShader(false);
1596 GL_DepthRange(0, 1);
1597 GL_PolygonOffset(0, 0);
1598 GL_DepthTest(false);
1599 if (v_color_enable.integer)
1601 c[0] = v_color_white_r.value;
1602 c[1] = v_color_white_g.value;
1603 c[2] = v_color_white_b.value;
1606 c[0] = c[1] = c[2] = v_contrast.value;
1607 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1609 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1610 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1612 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1613 R_Mesh_Draw(0, 3, 0, 1, NULL, polygonelements, 0, 0);
1614 VectorScale(c, 0.5, c);
1617 if (v_color_enable.integer)
1619 c[0] = v_color_black_r.value;
1620 c[1] = v_color_black_g.value;
1621 c[2] = v_color_black_b.value;
1624 c[0] = c[1] = c[2] = v_brightness.value;
1625 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1627 GL_BlendFunc(GL_ONE, GL_ONE);
1628 GL_Color(c[0], c[1], c[2], 1);
1629 R_Mesh_Draw(0, 3, 0, 1, NULL, polygonelements, 0, 0);