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, 1 makes them all black)"};
34 extern cvar_t v_glslgamma;
36 //=============================================================================
37 /* Support Routines */
39 #define FONT_FILESIZE 13468
40 #define MAX_CACHED_PICS 1024
41 #define CACHEPICHASHSIZE 256
42 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
43 static cachepic_t cachepics[MAX_CACHED_PICS];
44 static int numcachepics;
46 static rtexturepool_t *drawtexturepool;
48 static const unsigned char concharimage[FONT_FILESIZE] =
53 static rtexture_t *draw_generateconchars(void)
56 unsigned char buffer[65536][4], *data = NULL;
59 data = LoadTGA_BGRA (concharimage, FONT_FILESIZE);
61 for (i = 0;i < 8192;i++)
63 random = lhrandom (0.0,1.0);
64 buffer[i][2] = 83 + (unsigned char)(random * 64);
65 buffer[i][1] = 71 + (unsigned char)(random * 32);
66 buffer[i][0] = 23 + (unsigned char)(random * 16);
67 buffer[i][3] = data[i*4+0];
70 for (i = 8192;i < 32768;i++)
72 random = lhrandom (0.0,1.0);
73 buffer[i][2] = 95 + (unsigned char)(random * 64);
74 buffer[i][1] = 95 + (unsigned char)(random * 64);
75 buffer[i][0] = 95 + (unsigned char)(random * 64);
76 buffer[i][3] = data[i*4+0];
79 for (i = 32768;i < 40960;i++)
81 random = lhrandom (0.0,1.0);
82 buffer[i][2] = 83 + (unsigned char)(random * 64);
83 buffer[i][1] = 71 + (unsigned char)(random * 32);
84 buffer[i][0] = 23 + (unsigned char)(random * 16);
85 buffer[i][3] = data[i*4+0];
88 for (i = 40960;i < 65536;i++)
90 random = lhrandom (0.0,1.0);
91 buffer[i][2] = 96 + (unsigned char)(random * 64);
92 buffer[i][1] = 43 + (unsigned char)(random * 32);
93 buffer[i][0] = 27 + (unsigned char)(random * 32);
94 buffer[i][3] = data[i*4+0];
98 Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, &buffer[0][0]);
102 return R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, &buffer[0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
105 static rtexture_t *draw_generateditherpattern(void)
108 unsigned char pixels[8][8];
109 for (y = 0;y < 8;y++)
110 for (x = 0;x < 8;x++)
111 pixels[y][x] = ((x^y) & 4) ? 254 : 0;
112 return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST | TEXF_PRECACHE, palette_bgra_transparent);
115 typedef struct embeddedpic_s
124 static const embeddedpic_t embeddedpics[] =
127 "gfx/prydoncursor001", 16, 16,
146 "ui/mousepointer", 16, 16,
165 "gfx/crosshair1", 16, 16,
184 "gfx/crosshair2", 16, 16,
203 "gfx/crosshair3", 16, 16,
222 "gfx/crosshair4", 16, 16,
241 "gfx/crosshair5", 8, 8,
252 "gfx/crosshair6", 2, 2,
257 "gfx/crosshair7", 16, 16,
276 "gfx/editlights/cursor", 16, 16,
295 "gfx/editlights/light", 16, 16,
314 "gfx/editlights/noshadow", 16, 16,
333 "gfx/editlights/selection", 16, 16,
352 "gfx/editlights/cubemaplight", 16, 16,
371 "gfx/editlights/cubemapnoshadowlight", 16, 16,
392 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
394 const embeddedpic_t *p;
395 for (p = embeddedpics;p->name;p++)
396 if (!strcmp(name, p->name))
397 return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA | TEXF_PRECACHE, palette_bgra_embeddedpic);
398 if (!strcmp(name, "gfx/conchars"))
399 return draw_generateconchars();
400 if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
401 return draw_generateditherpattern();
403 Con_Printf("Draw_CachePic: failed to load %s\n", name);
404 return r_texture_notexture;
413 // FIXME: move this to client somehow
414 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
420 unsigned char *lmpdata;
421 char lmpname[MAX_QPATH];
423 // check whether the picture has already been cached
424 crc = CRC_Block((unsigned char *)path, strlen(path));
425 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
426 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
427 if (!strcmp (path, pic->name))
430 if (numcachepics == MAX_CACHED_PICS)
432 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
433 // FIXME: support NULL in callers?
434 return cachepics; // return the first one
436 pic = cachepics + (numcachepics++);
437 strlcpy (pic->name, path, sizeof(pic->name));
439 pic->chain = cachepichash[hashkey];
440 cachepichash[hashkey] = pic;
442 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
443 pic->tex = CL_GetDynTexture( path );
444 // if so, set the width/height, too
446 pic->width = R_TextureWidth(pic->tex);
447 pic->height = R_TextureHeight(pic->tex);
448 // we're done now (early-out)
453 if (!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
454 flags |= TEXF_PRECACHE;
455 if (strcmp(path, "gfx/colorcontrol/ditherpattern"))
457 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer)
458 flags |= TEXF_COMPRESS;
460 // load a high quality image from disk if possible
461 pic->tex = loadtextureimage(drawtexturepool, path, false, flags, true);
462 if (pic->tex == NULL && !strncmp(path, "gfx/", 4))
464 // compatibility with older versions which did not require gfx/ prefix
465 pic->tex = loadtextureimage(drawtexturepool, path + 4, false, flags, true);
467 // if a high quality image was loaded, set the pic's size to match it, just
468 // in case there's no low quality version to get the size from
471 pic->width = R_TextureWidth(pic->tex);
472 pic->height = R_TextureHeight(pic->tex);
475 // now read the low quality version (wad or lmp file), and take the pic
476 // size from that even if we don't upload the texture, this way the pics
477 // show up the right size in the menu even if they were replaced with
478 // higher or lower resolution versions
479 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
480 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
482 if (developer_loading.integer)
483 Con_Printf("loading lump \"%s\"\n", path);
487 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
488 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
489 // if no high quality replacement image was found, upload the original low quality texture
491 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
495 else if ((lmpdata = W_GetLumpName (path + 4)))
497 if (developer_loading.integer)
498 Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
500 if (!strcmp(path, "gfx/conchars"))
502 // conchars is a raw image and with color 0 as transparent instead of 255
505 // if no high quality replacement image was found, upload the original low quality texture
507 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_font);
511 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
512 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
513 // if no high quality replacement image was found, upload the original low quality texture
515 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
519 // if it's not found on disk, generate an image
520 if (pic->tex == NULL)
522 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
523 pic->width = R_TextureWidth(pic->tex);
524 pic->height = R_TextureHeight(pic->tex);
530 cachepic_t *Draw_CachePic (const char *path)
532 return Draw_CachePic_Flags (path, 0);
535 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
540 crc = CRC_Block((unsigned char *)picname, strlen(picname));
541 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
542 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
543 if (!strcmp (picname, pic->name))
548 if (pic->tex && pic->width == width && pic->height == height)
550 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
558 if (numcachepics == MAX_CACHED_PICS)
560 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
561 // FIXME: support NULL in callers?
562 return cachepics; // return the first one
564 pic = cachepics + (numcachepics++);
565 strlcpy (pic->name, picname, sizeof(pic->name));
567 pic->chain = cachepichash[hashkey];
568 cachepichash[hashkey] = pic;
573 pic->height = height;
575 R_FreeTexture(pic->tex);
576 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, alpha ? TEXF_ALPHA : 0, NULL);
580 void Draw_FreePic(const char *picname)
585 // this doesn't really free the pic, but does free it's texture
586 crc = CRC_Block((unsigned char *)picname, strlen(picname));
587 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
588 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
590 if (!strcmp (picname, pic->name) && pic->tex)
592 R_FreeTexture(pic->tex);
600 extern int con_linewidth; // to force rewrapping
601 static void LoadFont(qboolean override, const char *name, dp_font_t *fnt)
605 char widthfile[MAX_QPATH];
607 fs_offset_t widthbufsize;
609 if(override || !fnt->texpath[0])
610 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
612 if(drawtexturepool == NULL)
613 return; // before gl_draw_start, so will be loaded later
615 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
616 if(fnt->tex == r_texture_notexture)
618 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
619 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
622 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
624 // unspecified width == 1 (base width)
625 for(i = 1; i < 256; ++i)
626 fnt->width_of[i] = 1;
628 // FIXME load "name.width", if it fails, fill all with 1
629 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
631 float extraspacing = 0;
632 const char *p = widthbuf;
637 if(!COM_ParseToken_Simple(&p, false, false))
640 if(!strcmp(com_token, "extraspacing"))
642 if(!COM_ParseToken_Simple(&p, false, false))
644 extraspacing = atof(com_token);
647 fnt->width_of[ch++] = atof(com_token) + extraspacing;
653 maxwidth = fnt->width_of[1];
654 for(i = 2; i < 256; ++i)
655 maxwidth = max(maxwidth, fnt->width_of[i]);
656 fnt->width_of[0] = maxwidth;
658 if(fnt == FONT_CONSOLE)
659 con_linewidth = -1; // rewrap console in next frame
662 static dp_font_t *FindFont(const char *title)
665 for(i = 0; i < MAX_FONTS; ++i)
666 if(!strcmp(dp_fonts[i].title, title))
671 static void LoadFont_f(void)
677 Con_Printf("Available font commands:\n");
678 for(i = 0; i < MAX_FONTS; ++i)
679 Con_Printf(" loadfont %s gfx/tgafile\n", dp_fonts[i].title);
682 f = FindFont(Cmd_Argv(1));
685 Con_Printf("font function not found\n");
688 LoadFont(true, (Cmd_Argc() < 3) ? "gfx/conchars" : Cmd_Argv(2), f);
696 static void gl_draw_start(void)
699 drawtexturepool = R_AllocTexturePool();
702 memset(cachepichash, 0, sizeof(cachepichash));
704 for(i = 0; i < MAX_FONTS; ++i)
705 LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i]);
707 // draw the loading screen so people have something to see in the newly opened window
708 SCR_UpdateLoadingScreen(true);
711 static void gl_draw_shutdown(void)
713 R_FreeTexturePool(&drawtexturepool);
716 memset(cachepichash, 0, sizeof(cachepichash));
719 static void gl_draw_newmap(void)
723 void GL_Draw_Init (void)
726 Cvar_RegisterVariable(&r_textshadow);
727 Cvar_RegisterVariable(&r_textbrightness);
728 Cvar_RegisterVariable(&r_textcontrast);
729 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
730 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
732 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
733 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
734 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
735 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
736 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
737 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
738 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
739 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
740 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
741 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
742 if(!FONT_USER[i].title[0])
743 dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++);
746 void _DrawQ_Setup(void)
748 if (r_refdef.draw2dstage)
750 r_refdef.draw2dstage = true;
752 qglViewport(r_refdef.view.x, vid.height - (r_refdef.view.y + r_refdef.view.height), r_refdef.view.width, r_refdef.view.height);CHECKGLERROR
753 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
754 GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
755 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
756 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
757 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
758 R_Mesh_Matrix(&identitymatrix);
762 GL_PolygonOffset(0, 0);
766 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
768 R_SetupGenericShader(true);
771 static void _DrawQ_ProcessDrawFlag(int flags)
775 if(flags == DRAWFLAG_ADDITIVE)
776 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
777 else if(flags == DRAWFLAG_MODULATE)
778 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
779 else if(flags == DRAWFLAG_2XMODULATE)
780 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
782 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
785 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
789 _DrawQ_ProcessDrawFlag(flags);
790 GL_Color(red, green, blue, alpha);
792 R_Mesh_VertexPointer(floats, 0, 0);
793 R_Mesh_ColorPointer(NULL, 0, 0);
794 R_Mesh_ResetTextureState();
795 R_SetupGenericShader(pic != NULL);
801 height = pic->height;
802 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
803 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
806 floats[12] = 0.0f;floats[13] = 0.0f;
807 floats[14] = 1.0f;floats[15] = 0.0f;
808 floats[16] = 1.0f;floats[17] = 1.0f;
809 floats[18] = 0.0f;floats[19] = 1.0f;
811 // AK07: lets be texel correct on the corners
813 float horz_offset = 0.5f / pic->width;
814 float vert_offset = 0.5f / pic->height;
816 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
817 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
818 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
819 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
824 floats[2] = floats[5] = floats[8] = floats[11] = 0;
825 floats[0] = floats[9] = x;
826 floats[1] = floats[4] = y;
827 floats[3] = floats[6] = x + width;
828 floats[7] = floats[10] = y + height;
830 R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
833 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
837 _DrawQ_ProcessDrawFlag(flags);
838 GL_Color(red, green, blue, alpha);
840 R_Mesh_VertexPointer(floats, 0, 0);
841 R_Mesh_ColorPointer(NULL, 0, 0);
842 R_Mesh_ResetTextureState();
843 R_SetupGenericShader(false);
845 floats[2] = floats[5] = floats[8] = floats[11] = 0;
846 floats[0] = floats[9] = x;
847 floats[1] = floats[4] = y;
848 floats[3] = floats[6] = x + width;
849 floats[7] = floats[10] = y + height;
851 R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
854 // color tag printing
855 static const vec4_t string_colors[] =
858 // LordHavoc: why on earth is cyan before magenta in Quake3?
859 // LordHavoc: note: Doom3 uses white for [0] and [7]
860 {0.0, 0.0, 0.0, 1.0}, // black
861 {1.0, 0.0, 0.0, 1.0}, // red
862 {0.0, 1.0, 0.0, 1.0}, // green
863 {1.0, 1.0, 0.0, 1.0}, // yellow
864 {0.0, 0.0, 1.0, 1.0}, // blue
865 {0.0, 1.0, 1.0, 1.0}, // cyan
866 {1.0, 0.0, 1.0, 1.0}, // magenta
867 {1.0, 1.0, 1.0, 1.0}, // white
868 // [515]'s BX_COLOREDTEXT extension
869 {1.0, 1.0, 1.0, 0.5}, // half transparent
870 {0.5, 0.5, 0.5, 1.0} // half brightness
871 // Black's color table
872 //{1.0, 1.0, 1.0, 1.0},
873 //{1.0, 0.0, 0.0, 1.0},
874 //{0.0, 1.0, 0.0, 1.0},
875 //{0.0, 0.0, 1.0, 1.0},
876 //{1.0, 1.0, 0.0, 1.0},
877 //{0.0, 1.0, 1.0, 1.0},
878 //{1.0, 0.0, 1.0, 1.0},
879 //{0.1, 0.1, 0.1, 1.0}
882 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
884 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
886 float C = r_textcontrast.value;
887 float B = r_textbrightness.value;
888 Vector4Copy(string_colors[colorindex], color);
889 Vector4Set(color, (color[0] * C + B) * r, (color[1] * C + B) * g, (color[2] * C + B) * b, color[3] * a);
892 float shadowalpha = color[0]+color[1]+color[2] * 0.8;
893 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
897 float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
899 int num, colorindex = STRING_COLOR_DEFAULT;
906 if (!outcolor || *outcolor == -1)
907 colorindex = STRING_COLOR_DEFAULT;
909 colorindex = *outcolor;
911 for (i = 0;i < *maxlen && text[i];i++)
915 if(x + fnt->width_of[' '] > maxwidth)
916 break; // oops, can't draw this
917 x += fnt->width_of[' '];
920 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen)
922 if (text[i+1] == STRING_COLOR_TAG)
926 else if (text[i+1] >= '0' && text[i+1] <= '9')
928 colorindex = text[i+1] - '0';
933 num = (unsigned char) text[i];
934 if(x + fnt->width_of[num] > maxwidth)
935 break; // oops, can't draw this
936 x += fnt->width_of[num];
942 *outcolor = colorindex;
947 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)
949 int num, shadow, colorindex = STRING_COLOR_DEFAULT;
951 float x = startx, y, s, t, u, v, thisw;
955 float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
956 float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
957 float color4f[QUADELEMENTS_MAXQUADS*4*4];
960 tw = R_TextureWidth(fnt->tex);
961 th = R_TextureHeight(fnt->tex);
966 _DrawQ_ProcessDrawFlag(flags);
968 R_Mesh_ColorPointer(color4f, 0, 0);
969 R_Mesh_ResetTextureState();
970 R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
971 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
972 R_Mesh_VertexPointer(vertex3f, 0, 0);
973 R_SetupGenericShader(true);
980 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
982 if (!outcolor || *outcolor == -1)
983 colorindex = STRING_COLOR_DEFAULT;
985 colorindex = *outcolor;
986 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
992 x += r_textshadow.value;
993 y += r_textshadow.value;
995 for (i = 0;i < maxlen && text[i];i++)
999 x += fnt->width_of[' '] * w;
1002 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen)
1004 if (text[i+1] == STRING_COLOR_TAG)
1008 else if (text[i+1] >= '0' && text[i+1] <= '9')
1010 colorindex = text[i+1] - '0';
1011 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
1016 num = (unsigned char) text[i];
1017 thisw = fnt->width_of[num];
1018 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1019 s = (num & 15)*0.0625f + (0.5f / tw);
1020 t = (num >> 4)*0.0625f + (0.5f / th);
1021 u = 0.0625f * thisw - (1.0f / tw);
1022 v = 0.0625f - (1.0f / th);
1023 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1024 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1025 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1026 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1027 at[ 0] = s ;at[ 1] = t ;
1028 at[ 2] = s+u;at[ 3] = t ;
1029 at[ 4] = s+u;at[ 5] = t+v;
1030 at[ 6] = s ;at[ 7] = t+v;
1031 av[ 0] = x ;av[ 1] = y ;av[ 2] = 10;
1032 av[ 3] = x+w*thisw;av[ 4] = y ;av[ 5] = 10;
1033 av[ 6] = x+w*thisw;av[ 7] = y+h;av[ 8] = 10;
1034 av[ 9] = x ;av[10] = y+h;av[11] = 10;
1039 if (batchcount >= QUADELEMENTS_MAXQUADS)
1041 GL_LockArrays(0, batchcount * 4);
1042 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
1043 GL_LockArrays(0, 0);
1054 GL_LockArrays(0, batchcount * 4);
1055 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
1056 GL_LockArrays(0, 0);
1060 *outcolor = colorindex;
1062 // note: this relies on the proper text (not shadow) being drawn last
1066 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)
1068 return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]);
1071 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1073 return DrawQ_TextWidth_Font_UntilWidth_TrackColors(text, maxlen, NULL, ignorecolorcodes, fnt, maxWidth);
1076 float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt)
1078 return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, ignorecolorcodes, fnt, 1000000000);
1083 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1085 int color, numchars = 0;
1086 char *outputend2c = output2c + maxoutchars - 2;
1087 if (!outcolor || *outcolor == -1)
1088 color = STRING_COLOR_DEFAULT;
1092 maxreadchars = 1<<30;
1093 textend = text + maxreadchars;
1094 while (text != textend && *text)
1096 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1098 if (text[1] == STRING_COLOR_TAG)
1100 else if (text[1] >= '0' && text[1] <= '9')
1102 color = text[1] - '0';
1107 if (output2c >= outputend2c)
1109 *output2c++ = *text++;
1110 *output2c++ = color;
1113 output2c[0] = output2c[1] = 0;
1120 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)
1124 _DrawQ_ProcessDrawFlag(flags);
1126 R_Mesh_VertexPointer(floats, 0, 0);
1127 R_Mesh_ColorPointer(floats + 20, 0, 0);
1128 R_Mesh_ResetTextureState();
1129 R_SetupGenericShader(pic != NULL);
1135 height = pic->height;
1136 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1137 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1138 floats[12] = s1;floats[13] = t1;
1139 floats[14] = s2;floats[15] = t2;
1140 floats[16] = s4;floats[17] = t4;
1141 floats[18] = s3;floats[19] = t3;
1144 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1145 floats[0] = floats[9] = x;
1146 floats[1] = floats[4] = y;
1147 floats[3] = floats[6] = x + width;
1148 floats[7] = floats[10] = y + height;
1149 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1150 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1151 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1152 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1154 R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
1157 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1159 _DrawQ_ProcessDrawFlag(flags);
1161 R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1162 R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1163 R_Mesh_ResetTextureState();
1164 R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
1165 R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1166 R_SetupGenericShader(mesh->texture != NULL);
1168 GL_LockArrays(0, mesh->num_vertices);
1169 R_Mesh_Draw(0, mesh->num_vertices, mesh->num_triangles, mesh->data_element3i, 0, 0);
1170 GL_LockArrays(0, 0);
1173 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1177 _DrawQ_ProcessDrawFlag(flags);
1181 qglBegin(GL_LINE_LOOP);
1182 for (num = 0;num < mesh->num_vertices;num++)
1184 if (mesh->data_color4f)
1185 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]);
1186 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1192 //[515]: this is old, delete
1193 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1195 _DrawQ_ProcessDrawFlag(flags);
1198 qglLineWidth(width);CHECKGLERROR
1200 GL_Color(r,g,b,alpha);
1203 qglVertex2f(x1, y1);
1204 qglVertex2f(x2, y2);
1209 void DrawQ_SetClipArea(float x, float y, float width, float height)
1213 // We have to convert the con coords into real coords
1214 // OGL uses top to bottom
1215 GL_Scissor((int)(x * ((float)vid.width / vid_conwidth.integer)), (int)(y * ((float) vid.height / vid_conheight.integer)), (int)(width * ((float)vid.width / vid_conwidth.integer)), (int)(height * ((float)vid.height / vid_conheight.integer)));
1217 GL_ScissorTest(true);
1220 void DrawQ_ResetClipArea(void)
1223 GL_ScissorTest(false);
1226 void DrawQ_Finish(void)
1228 r_refdef.draw2dstage = false;
1231 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1232 void R_DrawGamma(void)
1235 if (!vid_usinghwgamma && !(r_glsl.integer && v_glslgamma.integer))
1237 // all the blends ignore depth
1238 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1239 R_Mesh_ColorPointer(NULL, 0, 0);
1240 R_Mesh_ResetTextureState();
1241 R_SetupGenericShader(false);
1243 GL_DepthRange(0, 1);
1244 GL_PolygonOffset(0, 0);
1245 GL_DepthTest(false);
1246 if (v_color_enable.integer)
1248 c[0] = v_color_white_r.value;
1249 c[1] = v_color_white_g.value;
1250 c[2] = v_color_white_b.value;
1253 c[0] = c[1] = c[2] = v_contrast.value;
1254 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1256 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1257 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1259 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1260 R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);
1261 VectorScale(c, 0.5, c);
1264 if (v_color_enable.integer)
1266 c[0] = v_color_black_r.value;
1267 c[1] = v_color_black_g.value;
1268 c[2] = v_color_black_b.value;
1271 c[0] = c[1] = c[2] = v_brightness.value;
1272 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1274 GL_BlendFunc(GL_ONE, GL_ONE);
1275 GL_Color(c[0], c[1], c[2], 1);
1276 R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);