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)
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();
402 Con_Printf("Draw_CachePic: failed to load %s\n", name);
403 return r_texture_notexture;
412 // FIXME: move this to client somehow
413 static cachepic_t *Draw_CachePic_Compression (const char *path, qboolean persistent, qboolean allow_compression)
419 unsigned char *lmpdata;
420 char lmpname[MAX_QPATH];
422 // check whether the picture has already been cached
423 crc = CRC_Block((unsigned char *)path, strlen(path));
424 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
425 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
426 if (!strcmp (path, pic->name))
429 if (numcachepics == MAX_CACHED_PICS)
431 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
432 // FIXME: support NULL in callers?
433 return cachepics; // return the first one
435 pic = cachepics + (numcachepics++);
436 strlcpy (pic->name, path, sizeof(pic->name));
438 pic->chain = cachepichash[hashkey];
439 cachepichash[hashkey] = pic;
441 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
442 pic->tex = CL_GetDynTexture( path );
443 // if so, set the width/height, too
445 pic->width = R_TextureWidth(pic->tex);
446 pic->height = R_TextureHeight(pic->tex);
447 // we're done now (early-out)
453 flags |= TEXF_PRECACHE;
454 if (strcmp(path, "gfx/colorcontrol/ditherpattern"))
456 if(allow_compression && gl_texturecompression_2d.integer)
457 flags |= TEXF_COMPRESS;
459 // load a high quality image from disk if possible
460 pic->tex = loadtextureimage(drawtexturepool, path, false, flags, true);
461 if (pic->tex == NULL && !strncmp(path, "gfx/", 4))
463 // compatibility with older versions which did not require gfx/ prefix
464 pic->tex = loadtextureimage(drawtexturepool, path + 4, false, flags, true);
466 // if a high quality image was loaded, set the pic's size to match it, just
467 // in case there's no low quality version to get the size from
470 pic->width = R_TextureWidth(pic->tex);
471 pic->height = R_TextureHeight(pic->tex);
474 // now read the low quality version (wad or lmp file), and take the pic
475 // size from that even if we don't upload the texture, this way the pics
476 // show up the right size in the menu even if they were replaced with
477 // higher or lower resolution versions
478 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
479 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
483 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
484 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
485 // if no high quality replacement image was found, upload the original low quality texture
487 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
491 else if ((lmpdata = W_GetLumpName (path + 4)))
493 if (!strcmp(path, "gfx/conchars"))
495 // conchars is a raw image and with color 0 as transparent instead of 255
498 // if no high quality replacement image was found, upload the original low quality texture
500 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_font);
504 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
505 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
506 // if no high quality replacement image was found, upload the original low quality texture
508 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
512 // if it's not found on disk, generate an image
513 if (pic->tex == NULL)
515 pic->tex = draw_generatepic(path);
516 pic->width = R_TextureWidth(pic->tex);
517 pic->height = R_TextureHeight(pic->tex);
522 cachepic_t *Draw_CachePic (const char *path, qboolean persistent)
524 return Draw_CachePic_Compression(path, persistent, true);
527 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
532 crc = CRC_Block((unsigned char *)picname, strlen(picname));
533 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
534 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
535 if (!strcmp (picname, pic->name))
540 if (pic->tex && pic->width == width && pic->height == height)
542 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
550 if (numcachepics == MAX_CACHED_PICS)
552 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
553 // FIXME: support NULL in callers?
554 return cachepics; // return the first one
556 pic = cachepics + (numcachepics++);
557 strlcpy (pic->name, picname, sizeof(pic->name));
559 pic->chain = cachepichash[hashkey];
560 cachepichash[hashkey] = pic;
565 pic->height = height;
567 R_FreeTexture(pic->tex);
568 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, alpha ? TEXF_ALPHA : 0, NULL);
572 void Draw_FreePic(const char *picname)
577 // this doesn't really free the pic, but does free it's texture
578 crc = CRC_Block((unsigned char *)picname, strlen(picname));
579 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
580 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
582 if (!strcmp (picname, pic->name) && pic->tex)
584 R_FreeTexture(pic->tex);
592 extern int con_linewidth; // to force rewrapping
593 static void LoadFont(qboolean override, const char *name, dp_font_t *fnt)
597 char widthfile[MAX_QPATH];
599 fs_offset_t widthbufsize;
601 if(override || !fnt->texpath[0])
602 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
604 if(drawtexturepool == NULL)
605 return; // before gl_draw_start, so will be loaded later
607 fnt->tex = Draw_CachePic_Compression(fnt->texpath, true, false)->tex;
608 if(fnt->tex == r_texture_notexture)
610 fnt->tex = Draw_CachePic_Compression("gfx/conchars", true, false)->tex;
611 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
614 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
616 // unspecified width == 1 (base width)
617 for(i = 1; i < 256; ++i)
618 fnt->width_of[i] = 1;
620 // FIXME load "name.width", if it fails, fill all with 1
621 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
623 float extraspacing = 0;
624 const char *p = widthbuf;
629 if(!COM_ParseToken_Simple(&p, false, false))
632 if(!strcmp(com_token, "extraspacing"))
634 if(!COM_ParseToken_Simple(&p, false, false))
636 extraspacing = atof(com_token);
639 fnt->width_of[ch++] = atof(com_token) + extraspacing;
645 maxwidth = fnt->width_of[1];
646 for(i = 2; i < 256; ++i)
647 maxwidth = max(maxwidth, fnt->width_of[i]);
648 fnt->width_of[0] = maxwidth;
650 if(fnt == FONT_CONSOLE)
651 con_linewidth = -1; // rewrap console in next frame
654 static dp_font_t *FindFont(const char *title)
657 for(i = 0; i < MAX_FONTS; ++i)
658 if(!strcmp(dp_fonts[i].title, title))
663 static void LoadFont_f(void)
669 Con_Printf("Available font commands:\n");
670 for(i = 0; i < MAX_FONTS; ++i)
671 Con_Printf(" loadfont %s gfx/tgafile\n", dp_fonts[i].title);
674 f = FindFont(Cmd_Argv(1));
677 Con_Printf("font function not found\n");
680 LoadFont(true, (Cmd_Argc() < 3) ? "gfx/conchars" : Cmd_Argv(2), f);
688 static void gl_draw_start(void)
691 drawtexturepool = R_AllocTexturePool();
694 memset(cachepichash, 0, sizeof(cachepichash));
696 for(i = 0; i < MAX_FONTS; ++i)
697 LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i]);
699 // draw the loading screen so people have something to see in the newly opened window
700 SCR_UpdateLoadingScreen(true);
703 static void gl_draw_shutdown(void)
705 R_FreeTexturePool(&drawtexturepool);
708 memset(cachepichash, 0, sizeof(cachepichash));
711 static void gl_draw_newmap(void)
715 void GL_Draw_Init (void)
718 Cvar_RegisterVariable(&r_textshadow);
719 Cvar_RegisterVariable(&r_textbrightness);
720 Cvar_RegisterVariable(&r_textcontrast);
721 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
722 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
724 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
725 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
726 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
727 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
728 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
729 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
730 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
731 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
732 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
733 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
734 if(!FONT_USER[i].title[0])
735 dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++);
738 void _DrawQ_Setup(void)
740 if (r_refdef.draw2dstage)
742 r_refdef.draw2dstage = true;
744 qglViewport(r_refdef.view.x, vid.height - (r_refdef.view.y + r_refdef.view.height), r_refdef.view.width, r_refdef.view.height);CHECKGLERROR
745 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
746 GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
747 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
748 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
749 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
750 R_Mesh_Matrix(&identitymatrix);
754 GL_PolygonOffset(0, 0);
758 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
760 R_SetupGenericShader(true);
763 static void _DrawQ_ProcessDrawFlag(int flags)
767 if(flags == DRAWFLAG_ADDITIVE)
768 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
769 else if(flags == DRAWFLAG_MODULATE)
770 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
771 else if(flags == DRAWFLAG_2XMODULATE)
772 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
774 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
777 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
781 _DrawQ_ProcessDrawFlag(flags);
782 GL_Color(red, green, blue, alpha);
784 R_Mesh_VertexPointer(floats, 0, 0);
785 R_Mesh_ColorPointer(NULL, 0, 0);
786 R_Mesh_ResetTextureState();
787 R_SetupGenericShader(pic != NULL);
793 height = pic->height;
794 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
795 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
798 floats[12] = 0.0f;floats[13] = 0.0f;
799 floats[14] = 1.0f;floats[15] = 0.0f;
800 floats[16] = 1.0f;floats[17] = 1.0f;
801 floats[18] = 0.0f;floats[19] = 1.0f;
803 // AK07: lets be texel correct on the corners
805 float horz_offset = 0.5f / pic->width;
806 float vert_offset = 0.5f / pic->height;
808 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
809 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
810 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
811 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
816 floats[2] = floats[5] = floats[8] = floats[11] = 0;
817 floats[0] = floats[9] = x;
818 floats[1] = floats[4] = y;
819 floats[3] = floats[6] = x + width;
820 floats[7] = floats[10] = y + height;
822 R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
825 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
829 _DrawQ_ProcessDrawFlag(flags);
830 GL_Color(red, green, blue, alpha);
832 R_Mesh_VertexPointer(floats, 0, 0);
833 R_Mesh_ColorPointer(NULL, 0, 0);
834 R_Mesh_ResetTextureState();
835 R_SetupGenericShader(false);
837 floats[2] = floats[5] = floats[8] = floats[11] = 0;
838 floats[0] = floats[9] = x;
839 floats[1] = floats[4] = y;
840 floats[3] = floats[6] = x + width;
841 floats[7] = floats[10] = y + height;
843 R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
846 // color tag printing
847 static const vec4_t string_colors[] =
850 // LordHavoc: why on earth is cyan before magenta in Quake3?
851 // LordHavoc: note: Doom3 uses white for [0] and [7]
852 {0.0, 0.0, 0.0, 1.0}, // black
853 {1.0, 0.0, 0.0, 1.0}, // red
854 {0.0, 1.0, 0.0, 1.0}, // green
855 {1.0, 1.0, 0.0, 1.0}, // yellow
856 {0.0, 0.0, 1.0, 1.0}, // blue
857 {0.0, 1.0, 1.0, 1.0}, // cyan
858 {1.0, 0.0, 1.0, 1.0}, // magenta
859 {1.0, 1.0, 1.0, 1.0}, // white
860 // [515]'s BX_COLOREDTEXT extension
861 {1.0, 1.0, 1.0, 0.5}, // half transparent
862 {0.5, 0.5, 0.5, 1.0} // half brightness
863 // Black's color table
864 //{1.0, 1.0, 1.0, 1.0},
865 //{1.0, 0.0, 0.0, 1.0},
866 //{0.0, 1.0, 0.0, 1.0},
867 //{0.0, 0.0, 1.0, 1.0},
868 //{1.0, 1.0, 0.0, 1.0},
869 //{0.0, 1.0, 1.0, 1.0},
870 //{1.0, 0.0, 1.0, 1.0},
871 //{0.1, 0.1, 0.1, 1.0}
874 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
876 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
878 float C = r_textcontrast.value;
879 float B = r_textbrightness.value;
880 Vector4Copy(string_colors[colorindex], color);
881 Vector4Set(color, (color[0] * C + B) * r, (color[1] * C + B) * g, (color[2] * C + B) * b, color[3] * a);
884 float shadowalpha = color[0]+color[1]+color[2] * 0.8;
885 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
889 float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
891 int num, colorindex = STRING_COLOR_DEFAULT;
898 if (!outcolor || *outcolor == -1)
899 colorindex = STRING_COLOR_DEFAULT;
901 colorindex = *outcolor;
903 for (i = 0;i < *maxlen && text[i];i++)
907 if(x + fnt->width_of[' '] > maxwidth)
908 break; // oops, can't draw this
909 x += fnt->width_of[' '];
912 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen)
914 if (text[i+1] == STRING_COLOR_TAG)
918 else if (text[i+1] >= '0' && text[i+1] <= '9')
920 colorindex = text[i+1] - '0';
925 num = (unsigned char) text[i];
926 if(x + fnt->width_of[num] > maxwidth)
927 break; // oops, can't draw this
928 x += fnt->width_of[num];
934 *outcolor = colorindex;
939 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)
941 int num, shadow, colorindex = STRING_COLOR_DEFAULT;
943 float x = startx, y, s, t, u, v;
947 float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
948 float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
949 float color4f[QUADELEMENTS_MAXQUADS*4*4];
954 _DrawQ_ProcessDrawFlag(flags);
956 R_Mesh_ColorPointer(color4f, 0, 0);
957 R_Mesh_ResetTextureState();
958 R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
959 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
960 R_Mesh_VertexPointer(vertex3f, 0, 0);
961 R_SetupGenericShader(true);
968 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
970 if (!outcolor || *outcolor == -1)
971 colorindex = STRING_COLOR_DEFAULT;
973 colorindex = *outcolor;
974 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
980 x += r_textshadow.value;
981 y += r_textshadow.value;
983 for (i = 0;i < maxlen && text[i];i++)
987 x += fnt->width_of[' '] * w;
990 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen)
992 if (text[i+1] == STRING_COLOR_TAG)
996 else if (text[i+1] >= '0' && text[i+1] <= '9')
998 colorindex = text[i+1] - '0';
999 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
1004 num = (unsigned char) text[i];
1005 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1006 s = (num & 15)*0.0625f + (0.5f / 256.0f);
1007 t = (num >> 4)*0.0625f + (0.5f / 256.0f);
1008 u = 0.0625f - (1.0f / 256.0f);
1009 v = 0.0625f - (1.0f / 256.0f);
1010 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1011 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1012 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1013 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1014 at[ 0] = s ;at[ 1] = t ;
1015 at[ 2] = s+u;at[ 3] = t ;
1016 at[ 4] = s+u;at[ 5] = t+v;
1017 at[ 6] = s ;at[ 7] = t+v;
1018 av[ 0] = x ;av[ 1] = y ;av[ 2] = 10;
1019 av[ 3] = x+w;av[ 4] = y ;av[ 5] = 10;
1020 av[ 6] = x+w;av[ 7] = y+h;av[ 8] = 10;
1021 av[ 9] = x ;av[10] = y+h;av[11] = 10;
1026 if (batchcount >= QUADELEMENTS_MAXQUADS)
1028 GL_LockArrays(0, batchcount * 4);
1029 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
1030 GL_LockArrays(0, 0);
1036 x += fnt->width_of[num] * w;
1041 GL_LockArrays(0, batchcount * 4);
1042 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
1043 GL_LockArrays(0, 0);
1047 *outcolor = colorindex;
1049 // note: this relies on the proper text (not shadow) being drawn last
1053 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)
1055 return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]);
1058 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1060 return DrawQ_TextWidth_Font_UntilWidth_TrackColors(text, maxlen, NULL, ignorecolorcodes, fnt, maxWidth);
1063 float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt)
1065 return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, ignorecolorcodes, fnt, 1000000000);
1070 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1072 int color, numchars = 0;
1073 char *outputend2c = output2c + maxoutchars - 2;
1074 if (!outcolor || *outcolor == -1)
1075 color = STRING_COLOR_DEFAULT;
1079 maxreadchars = 1<<30;
1080 textend = text + maxreadchars;
1081 while (text != textend && *text)
1083 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1085 if (text[1] == STRING_COLOR_TAG)
1087 else if (text[1] >= '0' && text[1] <= '9')
1089 color = text[1] - '0';
1094 if (output2c >= outputend2c)
1096 *output2c++ = *text++;
1097 *output2c++ = color;
1100 output2c[0] = output2c[1] = 0;
1107 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)
1111 _DrawQ_ProcessDrawFlag(flags);
1113 R_Mesh_VertexPointer(floats, 0, 0);
1114 R_Mesh_ColorPointer(floats + 20, 0, 0);
1115 R_Mesh_ResetTextureState();
1116 R_SetupGenericShader(pic != NULL);
1122 height = pic->height;
1123 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1124 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1125 floats[12] = s1;floats[13] = t1;
1126 floats[14] = s2;floats[15] = t2;
1127 floats[16] = s4;floats[17] = t4;
1128 floats[18] = s3;floats[19] = t3;
1131 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1132 floats[0] = floats[9] = x;
1133 floats[1] = floats[4] = y;
1134 floats[3] = floats[6] = x + width;
1135 floats[7] = floats[10] = y + height;
1136 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1137 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1138 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1139 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1141 R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
1144 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1146 _DrawQ_ProcessDrawFlag(flags);
1148 R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1149 R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1150 R_Mesh_ResetTextureState();
1151 R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
1152 R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1153 R_SetupGenericShader(mesh->texture != NULL);
1155 GL_LockArrays(0, mesh->num_vertices);
1156 R_Mesh_Draw(0, mesh->num_vertices, mesh->num_triangles, mesh->data_element3i, 0, 0);
1157 GL_LockArrays(0, 0);
1160 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1164 _DrawQ_ProcessDrawFlag(flags);
1168 qglBegin(GL_LINE_LOOP);
1169 for (num = 0;num < mesh->num_vertices;num++)
1171 if (mesh->data_color4f)
1172 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]);
1173 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1179 //[515]: this is old, delete
1180 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1182 _DrawQ_ProcessDrawFlag(flags);
1185 qglLineWidth(width);CHECKGLERROR
1187 GL_Color(r,g,b,alpha);
1190 qglVertex2f(x1, y1);
1191 qglVertex2f(x2, y2);
1196 void DrawQ_SetClipArea(float x, float y, float width, float height)
1200 // We have to convert the con coords into real coords
1201 // OGL uses top to bottom
1202 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)));
1204 GL_ScissorTest(true);
1207 void DrawQ_ResetClipArea(void)
1210 GL_ScissorTest(false);
1213 void DrawQ_Finish(void)
1215 r_refdef.draw2dstage = false;
1218 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1219 void R_DrawGamma(void)
1222 if (!vid_usinghwgamma && !(r_glsl.integer && v_glslgamma.integer))
1224 // all the blends ignore depth
1225 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1226 R_Mesh_ColorPointer(NULL, 0, 0);
1227 R_Mesh_ResetTextureState();
1228 R_SetupGenericShader(false);
1230 GL_DepthRange(0, 1);
1231 GL_PolygonOffset(0, 0);
1232 GL_DepthTest(false);
1233 if (v_color_enable.integer)
1235 c[0] = v_color_white_r.value;
1236 c[1] = v_color_white_g.value;
1237 c[2] = v_color_white_b.value;
1240 c[0] = c[1] = c[2] = v_contrast.value;
1241 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1243 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1244 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1246 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1247 R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);
1248 VectorScale(c, 0.5, c);
1251 if (v_color_enable.integer)
1253 c[0] = v_color_black_r.value;
1254 c[1] = v_color_black_g.value;
1255 c[2] = v_color_black_b.value;
1258 c[0] = c[1] = c[2] = v_brightness.value;
1259 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1261 GL_BlendFunc(GL_ONE, GL_ONE);
1262 GL_Color(c[0], c[1], c[2], 1);
1263 R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);