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 //=============================================================================
35 /* Support Routines */
37 #define FONT_FILESIZE 13468
38 #define MAX_CACHED_PICS 1024
39 #define CACHEPICHASHSIZE 256
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,
274 "gfx/editlights/cursor", 16, 16,
293 "gfx/editlights/light", 16, 16,
312 "gfx/editlights/noshadow", 16, 16,
331 "gfx/editlights/selection", 16, 16,
350 "gfx/editlights/cubemaplight", 16, 16,
369 "gfx/editlights/cubemapnoshadowlight", 16, 16,
390 static rtexture_t *draw_generatepic(const char *name)
392 const embeddedpic_t *p;
393 for (p = embeddedpics;p->name;p++)
394 if (!strcmp(name, p->name))
395 return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA | TEXF_PRECACHE, palette_bgra_embeddedpic);
396 if (!strcmp(name, "gfx/conchars"))
397 return draw_generateconchars();
398 if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
399 return draw_generateditherpattern();
400 Con_Printf("Draw_CachePic: failed to load %s\n", name);
401 return r_texture_notexture;
410 // FIXME: move this to client somehow
411 static cachepic_t *Draw_CachePic_Compression (const char *path, qboolean persistent, qboolean allow_compression)
417 unsigned char *lmpdata;
418 char lmpname[MAX_QPATH];
420 // check whether the picture has already been cached
421 crc = CRC_Block((unsigned char *)path, strlen(path));
422 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
423 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
424 if (!strcmp (path, pic->name))
427 if (numcachepics == MAX_CACHED_PICS)
429 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
430 // FIXME: support NULL in callers?
431 return cachepics; // return the first one
433 pic = cachepics + (numcachepics++);
434 strlcpy (pic->name, path, sizeof(pic->name));
436 pic->chain = cachepichash[hashkey];
437 cachepichash[hashkey] = pic;
439 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
440 pic->tex = CL_GetDynTexture( path );
441 // if so, set the width/height, too
443 pic->width = R_TextureWidth(pic->tex);
444 pic->height = R_TextureHeight(pic->tex);
445 // we're done now (early-out)
451 flags |= TEXF_PRECACHE;
452 if (strcmp(path, "gfx/colorcontrol/ditherpattern"))
454 if(allow_compression && gl_texturecompression_2d.integer)
455 flags |= TEXF_COMPRESS;
457 // load a high quality image from disk if possible
458 pic->tex = loadtextureimage(drawtexturepool, path, false, flags, true);
459 if (pic->tex == NULL && !strncmp(path, "gfx/", 4))
461 // compatibility with older versions which did not require gfx/ prefix
462 pic->tex = loadtextureimage(drawtexturepool, path + 4, false, flags, true);
464 // if a high quality image was loaded, set the pic's size to match it, just
465 // in case there's no low quality version to get the size from
468 pic->width = R_TextureWidth(pic->tex);
469 pic->height = R_TextureHeight(pic->tex);
472 // now read the low quality version (wad or lmp file), and take the pic
473 // size from that even if we don't upload the texture, this way the pics
474 // show up the right size in the menu even if they were replaced with
475 // higher or lower resolution versions
476 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
477 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
481 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
482 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
483 // if no high quality replacement image was found, upload the original low quality texture
485 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
489 else if ((lmpdata = W_GetLumpName (path + 4)))
491 if (!strcmp(path, "gfx/conchars"))
493 // conchars is a raw image and with color 0 as transparent instead of 255
496 // if no high quality replacement image was found, upload the original low quality texture
498 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_font);
502 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
503 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
504 // if no high quality replacement image was found, upload the original low quality texture
506 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
510 // if it's not found on disk, generate an image
511 if (pic->tex == NULL)
513 pic->tex = draw_generatepic(path);
514 pic->width = R_TextureWidth(pic->tex);
515 pic->height = R_TextureHeight(pic->tex);
520 cachepic_t *Draw_CachePic (const char *path, qboolean persistent)
522 return Draw_CachePic_Compression(path, persistent, true);
525 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
530 crc = CRC_Block((unsigned char *)picname, strlen(picname));
531 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
532 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
533 if (!strcmp (picname, pic->name))
538 if (pic->tex && pic->width == width && pic->height == height)
540 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
548 if (numcachepics == MAX_CACHED_PICS)
550 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
551 // FIXME: support NULL in callers?
552 return cachepics; // return the first one
554 pic = cachepics + (numcachepics++);
555 strlcpy (pic->name, picname, sizeof(pic->name));
557 pic->chain = cachepichash[hashkey];
558 cachepichash[hashkey] = pic;
563 pic->height = height;
565 R_FreeTexture(pic->tex);
566 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, alpha ? TEXF_ALPHA : 0, NULL);
570 void Draw_FreePic(const char *picname)
575 // this doesn't really free the pic, but does free it's texture
576 crc = CRC_Block((unsigned char *)picname, strlen(picname));
577 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
578 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
580 if (!strcmp (picname, pic->name) && pic->tex)
582 R_FreeTexture(pic->tex);
590 extern int con_linewidth; // to force rewrapping
591 static void LoadFont(qboolean override, const char *name, dp_font_t *fnt)
595 char widthfile[MAX_QPATH];
597 fs_offset_t widthbufsize;
599 if(override || !fnt->texpath[0])
600 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
602 if(drawtexturepool == NULL)
603 return; // before gl_draw_start, so will be loaded later
605 fnt->tex = Draw_CachePic_Compression(fnt->texpath, true, false)->tex;
606 if(fnt->tex == r_texture_notexture)
608 fnt->tex = Draw_CachePic_Compression("gfx/conchars", true, false)->tex;
609 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
612 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
614 // unspecified width == 1 (base width)
615 for(i = 1; i < 256; ++i)
616 fnt->width_of[i] = 1;
618 // FIXME load "name.width", if it fails, fill all with 1
619 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
621 float extraspacing = 0;
622 const char *p = widthbuf;
627 if(!COM_ParseToken_Simple(&p, false, false))
630 if(!strcmp(com_token, "extraspacing"))
632 if(!COM_ParseToken_Simple(&p, false, false))
634 extraspacing = atof(com_token);
637 fnt->width_of[ch++] = atof(com_token) + extraspacing;
643 maxwidth = fnt->width_of[1];
644 for(i = 2; i < 256; ++i)
645 maxwidth = max(maxwidth, fnt->width_of[i]);
646 fnt->width_of[0] = maxwidth;
648 if(fnt == FONT_CONSOLE)
649 con_linewidth = -1; // rewrap console in next frame
652 static dp_font_t *FindFont(const char *title)
655 for(i = 0; i < MAX_FONTS; ++i)
656 if(!strcmp(dp_fonts[i].title, title))
661 static void LoadFont_f(void)
667 Con_Printf("Available font commands:\n");
668 for(i = 0; i < MAX_FONTS; ++i)
669 Con_Printf(" loadfont %s gfx/tgafile\n", dp_fonts[i].title);
672 f = FindFont(Cmd_Argv(1));
675 Con_Printf("font function not found\n");
678 LoadFont(true, (Cmd_Argc() < 3) ? "gfx/conchars" : Cmd_Argv(2), f);
686 static void gl_draw_start(void)
689 drawtexturepool = R_AllocTexturePool();
692 memset(cachepichash, 0, sizeof(cachepichash));
694 for(i = 0; i < MAX_FONTS; ++i)
695 LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i]);
697 // draw the loading screen so people have something to see in the newly opened window
698 SCR_UpdateLoadingScreen(true);
701 static void gl_draw_shutdown(void)
703 R_FreeTexturePool(&drawtexturepool);
706 memset(cachepichash, 0, sizeof(cachepichash));
709 static void gl_draw_newmap(void)
713 void GL_Draw_Init (void)
716 Cvar_RegisterVariable(&r_textshadow);
717 Cvar_RegisterVariable(&r_textbrightness);
718 Cvar_RegisterVariable(&r_textcontrast);
719 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
720 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
722 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
723 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
724 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
725 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
726 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
727 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
728 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
729 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
730 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
731 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
732 if(!FONT_USER[i].title[0])
733 dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++);
736 void _DrawQ_Setup(void)
738 if (r_refdef.draw2dstage)
740 r_refdef.draw2dstage = true;
742 qglViewport(r_refdef.view.x, vid.height - (r_refdef.view.y + r_refdef.view.height), r_refdef.view.width, r_refdef.view.height);CHECKGLERROR
743 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
744 GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
745 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
746 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
747 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
748 R_Mesh_Matrix(&identitymatrix);
752 GL_PolygonOffset(0, 0);
756 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
758 R_SetupGenericShader(true);
761 static void _DrawQ_ProcessDrawFlag(int flags)
765 if(flags == DRAWFLAG_ADDITIVE)
766 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
767 else if(flags == DRAWFLAG_MODULATE)
768 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
769 else if(flags == DRAWFLAG_2XMODULATE)
770 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
772 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
775 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
779 _DrawQ_ProcessDrawFlag(flags);
780 GL_Color(red, green, blue, alpha);
782 R_Mesh_VertexPointer(floats, 0, 0);
783 R_Mesh_ColorPointer(NULL, 0, 0);
784 R_Mesh_ResetTextureState();
785 R_SetupGenericShader(pic != NULL);
791 height = pic->height;
792 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
793 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
796 floats[12] = 0.0f;floats[13] = 0.0f;
797 floats[14] = 1.0f;floats[15] = 0.0f;
798 floats[16] = 1.0f;floats[17] = 1.0f;
799 floats[18] = 0.0f;floats[19] = 1.0f;
801 // AK07: lets be texel correct on the corners
803 float horz_offset = 0.5f / pic->width;
804 float vert_offset = 0.5f / pic->height;
806 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
807 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
808 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
809 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
814 floats[2] = floats[5] = floats[8] = floats[11] = 0;
815 floats[0] = floats[9] = x;
816 floats[1] = floats[4] = y;
817 floats[3] = floats[6] = x + width;
818 floats[7] = floats[10] = y + height;
820 R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
823 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
827 _DrawQ_ProcessDrawFlag(flags);
828 GL_Color(red, green, blue, alpha);
830 R_Mesh_VertexPointer(floats, 0, 0);
831 R_Mesh_ColorPointer(NULL, 0, 0);
832 R_Mesh_ResetTextureState();
833 R_SetupGenericShader(false);
835 floats[2] = floats[5] = floats[8] = floats[11] = 0;
836 floats[0] = floats[9] = x;
837 floats[1] = floats[4] = y;
838 floats[3] = floats[6] = x + width;
839 floats[7] = floats[10] = y + height;
841 R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
844 // color tag printing
845 static const vec4_t string_colors[] =
848 // LordHavoc: why on earth is cyan before magenta in Quake3?
849 // LordHavoc: note: Doom3 uses white for [0] and [7]
850 {0.0, 0.0, 0.0, 1.0}, // black
851 {1.0, 0.0, 0.0, 1.0}, // red
852 {0.0, 1.0, 0.0, 1.0}, // green
853 {1.0, 1.0, 0.0, 1.0}, // yellow
854 {0.0, 0.0, 1.0, 1.0}, // blue
855 {0.0, 1.0, 1.0, 1.0}, // cyan
856 {1.0, 0.0, 1.0, 1.0}, // magenta
857 {1.0, 1.0, 1.0, 1.0}, // white
858 // [515]'s BX_COLOREDTEXT extension
859 {1.0, 1.0, 1.0, 0.5}, // half transparent
860 {0.5, 0.5, 0.5, 1.0} // half brightness
861 // Black's color table
862 //{1.0, 1.0, 1.0, 1.0},
863 //{1.0, 0.0, 0.0, 1.0},
864 //{0.0, 1.0, 0.0, 1.0},
865 //{0.0, 0.0, 1.0, 1.0},
866 //{1.0, 1.0, 0.0, 1.0},
867 //{0.0, 1.0, 1.0, 1.0},
868 //{1.0, 0.0, 1.0, 1.0},
869 //{0.1, 0.1, 0.1, 1.0}
872 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
874 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
876 float C = r_textcontrast.value;
877 float B = r_textbrightness.value;
878 Vector4Copy(string_colors[colorindex], color);
879 Vector4Set(color, (color[0] * C + B) * r, (color[1] * C + B) * g, (color[2] * C + B) * b, color[3] * a);
882 float shadowalpha = color[0]+color[1]+color[2] * 0.8;
883 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
887 float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
889 int num, colorindex = STRING_COLOR_DEFAULT;
896 if (!outcolor || *outcolor == -1)
897 colorindex = STRING_COLOR_DEFAULT;
899 colorindex = *outcolor;
901 for (i = 0;i < *maxlen && text[i];i++)
905 if(x + fnt->width_of[' '] > maxwidth)
906 break; // oops, can't draw this
907 x += fnt->width_of[' '];
910 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen)
912 if (text[i+1] == STRING_COLOR_TAG)
916 else if (text[i+1] >= '0' && text[i+1] <= '9')
918 colorindex = text[i+1] - '0';
923 num = (unsigned char) text[i];
924 if(x + fnt->width_of[num] > maxwidth)
925 break; // oops, can't draw this
926 x += fnt->width_of[num];
932 *outcolor = colorindex;
937 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)
939 int num, shadow, colorindex = STRING_COLOR_DEFAULT;
941 float x = startx, y, s, t, u, v;
945 float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
946 float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
947 float color4f[QUADELEMENTS_MAXQUADS*4*4];
952 _DrawQ_ProcessDrawFlag(flags);
954 R_Mesh_ColorPointer(color4f, 0, 0);
955 R_Mesh_ResetTextureState();
956 R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
957 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
958 R_Mesh_VertexPointer(vertex3f, 0, 0);
959 R_SetupGenericShader(true);
966 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
968 if (!outcolor || *outcolor == -1)
969 colorindex = STRING_COLOR_DEFAULT;
971 colorindex = *outcolor;
972 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
978 x += r_textshadow.value;
979 y += r_textshadow.value;
981 for (i = 0;i < maxlen && text[i];i++)
985 x += fnt->width_of[' '] * w;
988 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen)
990 if (text[i+1] == STRING_COLOR_TAG)
994 else if (text[i+1] >= '0' && text[i+1] <= '9')
996 colorindex = text[i+1] - '0';
997 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
1002 num = (unsigned char) text[i];
1003 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1004 s = (num & 15)*0.0625f + (0.5f / 256.0f);
1005 t = (num >> 4)*0.0625f + (0.5f / 256.0f);
1006 u = 0.0625f - (1.0f / 256.0f);
1007 v = 0.0625f - (1.0f / 256.0f);
1008 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1009 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1010 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1011 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1012 at[ 0] = s ;at[ 1] = t ;
1013 at[ 2] = s+u;at[ 3] = t ;
1014 at[ 4] = s+u;at[ 5] = t+v;
1015 at[ 6] = s ;at[ 7] = t+v;
1016 av[ 0] = x ;av[ 1] = y ;av[ 2] = 10;
1017 av[ 3] = x+w;av[ 4] = y ;av[ 5] = 10;
1018 av[ 6] = x+w;av[ 7] = y+h;av[ 8] = 10;
1019 av[ 9] = x ;av[10] = y+h;av[11] = 10;
1024 if (batchcount >= QUADELEMENTS_MAXQUADS)
1026 GL_LockArrays(0, batchcount * 4);
1027 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
1028 GL_LockArrays(0, 0);
1034 x += fnt->width_of[num] * w;
1039 GL_LockArrays(0, batchcount * 4);
1040 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
1041 GL_LockArrays(0, 0);
1045 *outcolor = colorindex;
1047 // note: this relies on the proper text (not shadow) being drawn last
1051 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)
1053 return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]);
1056 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1058 return DrawQ_TextWidth_Font_UntilWidth_TrackColors(text, maxlen, NULL, ignorecolorcodes, fnt, maxWidth);
1061 float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt)
1063 return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, ignorecolorcodes, fnt, 1000000000);
1068 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1070 int color, numchars = 0;
1071 char *outputend2c = output2c + maxoutchars - 2;
1072 if (!outcolor || *outcolor == -1)
1073 color = STRING_COLOR_DEFAULT;
1077 maxreadchars = 1<<30;
1078 textend = text + maxreadchars;
1079 while (text != textend && *text)
1081 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1083 if (text[1] == STRING_COLOR_TAG)
1085 else if (text[1] >= '0' && text[1] <= '9')
1087 color = text[1] - '0';
1092 if (output2c >= outputend2c)
1094 *output2c++ = *text++;
1095 *output2c++ = color;
1098 output2c[0] = output2c[1] = 0;
1105 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)
1109 _DrawQ_ProcessDrawFlag(flags);
1111 R_Mesh_VertexPointer(floats, 0, 0);
1112 R_Mesh_ColorPointer(floats + 20, 0, 0);
1113 R_Mesh_ResetTextureState();
1114 R_SetupGenericShader(pic != NULL);
1120 height = pic->height;
1121 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1122 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1123 floats[12] = s1;floats[13] = t1;
1124 floats[14] = s2;floats[15] = t2;
1125 floats[16] = s4;floats[17] = t4;
1126 floats[18] = s3;floats[19] = t3;
1129 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1130 floats[0] = floats[9] = x;
1131 floats[1] = floats[4] = y;
1132 floats[3] = floats[6] = x + width;
1133 floats[7] = floats[10] = y + height;
1134 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1135 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1136 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1137 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1139 R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
1142 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1144 _DrawQ_ProcessDrawFlag(flags);
1146 R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1147 R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1148 R_Mesh_ResetTextureState();
1149 R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
1150 R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1151 R_SetupGenericShader(mesh->texture != NULL);
1153 GL_LockArrays(0, mesh->num_vertices);
1154 R_Mesh_Draw(0, mesh->num_vertices, mesh->num_triangles, mesh->data_element3i, 0, 0);
1155 GL_LockArrays(0, 0);
1158 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1162 _DrawQ_ProcessDrawFlag(flags);
1166 qglBegin(GL_LINE_LOOP);
1167 for (num = 0;num < mesh->num_vertices;num++)
1169 if (mesh->data_color4f)
1170 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]);
1171 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1177 //[515]: this is old, delete
1178 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1180 _DrawQ_ProcessDrawFlag(flags);
1183 qglLineWidth(width);CHECKGLERROR
1185 GL_Color(r,g,b,alpha);
1188 qglVertex2f(x1, y1);
1189 qglVertex2f(x2, y2);
1194 void DrawQ_SetClipArea(float x, float y, float width, float height)
1198 // We have to convert the con coords into real coords
1199 // OGL uses top to bottom
1200 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)));
1202 GL_ScissorTest(true);
1205 void DrawQ_ResetClipArea(void)
1208 GL_ScissorTest(false);
1211 void DrawQ_Finish(void)
1213 r_refdef.draw2dstage = false;
1216 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1217 void R_DrawGamma(void)
1220 if (!vid_usinghwgamma)
1222 // all the blends ignore depth
1223 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1224 R_Mesh_ColorPointer(NULL, 0, 0);
1225 R_Mesh_ResetTextureState();
1226 R_SetupGenericShader(false);
1228 GL_DepthRange(0, 1);
1229 GL_PolygonOffset(0, 0);
1230 GL_DepthTest(false);
1231 if (v_color_enable.integer)
1233 c[0] = v_color_white_r.value;
1234 c[1] = v_color_white_g.value;
1235 c[2] = v_color_white_b.value;
1238 c[0] = c[1] = c[2] = v_contrast.value;
1239 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1241 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1242 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1244 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1245 R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);
1246 VectorScale(c, 0.5, c);
1249 if (v_color_enable.integer)
1251 c[0] = v_color_black_r.value;
1252 c[1] = v_color_black_g.value;
1253 c[2] = v_color_black_b.value;
1256 c[0] = c[1] = c[2] = v_brightness.value;
1257 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1259 GL_BlendFunc(GL_ONE, GL_ONE);
1260 GL_Color(c[0], c[1], c[2], 1);
1261 R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);