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)"};
33 //=============================================================================
34 /* Support Routines */
36 #define FONT_FILESIZE 13468
37 #define MAX_CACHED_PICS 1024
38 #define CACHEPICHASHSIZE 256
39 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
40 static cachepic_t cachepics[MAX_CACHED_PICS];
41 static int numcachepics;
43 static rtexturepool_t *drawtexturepool;
45 static unsigned char concharimage[FONT_FILESIZE] =
50 static rtexture_t *draw_generateconchars(void)
53 unsigned char buffer[65536][4], *data = NULL;
56 data = LoadTGA_BGRA (concharimage, FONT_FILESIZE);
58 for (i = 0;i < 8192;i++)
60 random = lhrandom (0.0,1.0);
61 buffer[i][2] = 83 + (unsigned char)(random * 64);
62 buffer[i][1] = 71 + (unsigned char)(random * 32);
63 buffer[i][0] = 23 + (unsigned char)(random * 16);
64 buffer[i][3] = data[i*4+0];
67 for (i = 8192;i < 32768;i++)
69 random = lhrandom (0.0,1.0);
70 buffer[i][2] = 95 + (unsigned char)(random * 64);
71 buffer[i][1] = 95 + (unsigned char)(random * 64);
72 buffer[i][0] = 95 + (unsigned char)(random * 64);
73 buffer[i][3] = data[i*4+0];
76 for (i = 32768;i < 40960;i++)
78 random = lhrandom (0.0,1.0);
79 buffer[i][2] = 83 + (unsigned char)(random * 64);
80 buffer[i][1] = 71 + (unsigned char)(random * 32);
81 buffer[i][0] = 23 + (unsigned char)(random * 16);
82 buffer[i][3] = data[i*4+0];
85 for (i = 40960;i < 65536;i++)
87 random = lhrandom (0.0,1.0);
88 buffer[i][2] = 96 + (unsigned char)(random * 64);
89 buffer[i][1] = 43 + (unsigned char)(random * 32);
90 buffer[i][0] = 27 + (unsigned char)(random * 32);
91 buffer[i][3] = data[i*4+0];
95 Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, &buffer[0][0]);
99 return R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, &buffer[0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
102 static rtexture_t *draw_generateditherpattern(void)
105 unsigned char pixels[8][8];
106 for (y = 0;y < 8;y++)
107 for (x = 0;x < 8;x++)
108 pixels[y][x] = ((x^y) & 4) ? 254 : 0;
109 return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST | TEXF_PRECACHE, NULL);
112 typedef struct embeddedpic_s
121 static embeddedpic_t embeddedpics[] =
124 "gfx/prydoncursor001", 16, 16,
143 "ui/mousepointer", 16, 16,
162 "gfx/crosshair1", 16, 16,
181 "gfx/crosshair2", 16, 16,
200 "gfx/crosshair3", 16, 16,
219 "gfx/crosshair4", 16, 16,
238 "gfx/crosshair5", 8, 8,
249 "gfx/crosshair6", 2, 2,
254 "gfx/crosshair7", 16, 16,
273 "gfx/editlights/cursor", 16, 16,
292 "gfx/editlights/light", 16, 16,
311 "gfx/editlights/noshadow", 16, 16,
330 "gfx/editlights/selection", 16, 16,
349 "gfx/editlights/cubemaplight", 16, 16,
368 "gfx/editlights/cubemapnoshadowlight", 16, 16,
389 static rtexture_t *draw_generatepic(const char *name)
392 for (p = embeddedpics;p->name;p++)
393 if (!strcmp(name, p->name))
394 return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA | TEXF_PRECACHE, palette_bgra_embeddedpic);
395 if (!strcmp(name, "gfx/conchars"))
396 return draw_generateconchars();
397 if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
398 return draw_generateditherpattern();
399 Con_Printf("Draw_CachePic: failed to load %s\n", name);
400 return r_texture_notexture;
409 // FIXME: move this to client somehow
410 static cachepic_t *Draw_CachePic_Compression (const char *path, qboolean persistent, qboolean allow_compression)
416 unsigned char *lmpdata;
417 char lmpname[MAX_QPATH];
419 // check whether the picture has already been cached
420 crc = CRC_Block((unsigned char *)path, strlen(path));
421 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
422 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
423 if (!strcmp (path, pic->name))
426 if (numcachepics == MAX_CACHED_PICS)
428 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
429 // FIXME: support NULL in callers?
430 return cachepics; // return the first one
432 pic = cachepics + (numcachepics++);
433 strlcpy (pic->name, path, sizeof(pic->name));
435 pic->chain = cachepichash[hashkey];
436 cachepichash[hashkey] = pic;
438 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
439 pic->tex = CL_GetDynTexture( path );
440 // if so, set the width/height, too
442 pic->width = R_TextureWidth(pic->tex);
443 pic->height = R_TextureHeight(pic->tex);
444 // we're done now (early-out)
450 flags |= TEXF_PRECACHE;
451 if (strcmp(path, "gfx/colorcontrol/ditherpattern"))
453 if(allow_compression && gl_texturecompression_2d.integer)
454 flags |= TEXF_COMPRESS;
456 // load a high quality image from disk if possible
457 pic->tex = loadtextureimage(drawtexturepool, path, false, flags, true);
458 if (pic->tex == NULL && !strncmp(path, "gfx/", 4))
460 // compatibility with older versions which did not require gfx/ prefix
461 pic->tex = loadtextureimage(drawtexturepool, path + 4, false, flags, true);
463 // if a high quality image was loaded, set the pic's size to match it, just
464 // in case there's no low quality version to get the size from
467 pic->width = R_TextureWidth(pic->tex);
468 pic->height = R_TextureHeight(pic->tex);
471 // now read the low quality version (wad or lmp file), and take the pic
472 // size from that even if we don't upload the texture, this way the pics
473 // show up the right size in the menu even if they were replaced with
474 // higher or lower resolution versions
475 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
476 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
480 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
481 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
482 // if no high quality replacement image was found, upload the original low quality texture
484 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
488 else if ((lmpdata = W_GetLumpName (path + 4)))
490 if (!strcmp(path, "gfx/conchars"))
492 // conchars is a raw image and with color 0 as transparent instead of 255
495 // if no high quality replacement image was found, upload the original low quality texture
497 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_font);
501 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
502 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
503 // if no high quality replacement image was found, upload the original low quality texture
505 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
509 // if it's not found on disk, generate an image
510 if (pic->tex == NULL)
512 pic->tex = draw_generatepic(path);
513 pic->width = R_TextureWidth(pic->tex);
514 pic->height = R_TextureHeight(pic->tex);
519 cachepic_t *Draw_CachePic (const char *path, qboolean persistent)
521 return Draw_CachePic_Compression(path, persistent, true);
524 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
529 crc = CRC_Block((unsigned char *)picname, strlen(picname));
530 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
531 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
532 if (!strcmp (picname, pic->name))
537 if (pic->tex && pic->width == width && pic->height == height)
539 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
547 if (numcachepics == MAX_CACHED_PICS)
549 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
550 // FIXME: support NULL in callers?
551 return cachepics; // return the first one
553 pic = cachepics + (numcachepics++);
554 strlcpy (pic->name, picname, sizeof(pic->name));
556 pic->chain = cachepichash[hashkey];
557 cachepichash[hashkey] = pic;
562 pic->height = height;
564 R_FreeTexture(pic->tex);
565 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, alpha ? TEXF_ALPHA : 0, NULL);
569 void Draw_FreePic(const char *picname)
574 // this doesn't really free the pic, but does free it's texture
575 crc = CRC_Block((unsigned char *)picname, strlen(picname));
576 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
577 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
579 if (!strcmp (picname, pic->name) && pic->tex)
581 R_FreeTexture(pic->tex);
589 extern int con_linewidth; // to force rewrapping
590 static void LoadFont(qboolean override, const char *name, dp_font_t *fnt)
594 char widthfile[MAX_QPATH];
596 fs_offset_t widthbufsize;
598 if(override || !fnt->texpath[0])
599 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
601 if(drawtexturepool == NULL)
602 return; // before gl_draw_start, so will be loaded later
604 fnt->tex = Draw_CachePic_Compression(fnt->texpath, true, false)->tex;
605 if(fnt->tex == r_texture_notexture)
607 fnt->tex = Draw_CachePic_Compression("gfx/conchars", true, false)->tex;
608 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
611 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
613 // unspecified width == 1 (base width)
614 for(i = 1; i < 256; ++i)
615 fnt->width_of[i] = 1;
617 // FIXME load "name.width", if it fails, fill all with 1
618 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
620 float extraspacing = 0;
621 const char *p = widthbuf;
626 if(!COM_ParseToken_Simple(&p, false, false))
629 if(!strcmp(com_token, "extraspacing"))
631 if(!COM_ParseToken_Simple(&p, false, false))
633 extraspacing = atof(com_token);
636 fnt->width_of[ch++] = atof(com_token) + extraspacing;
642 maxwidth = fnt->width_of[1];
643 for(i = 2; i < 256; ++i)
644 maxwidth = max(maxwidth, fnt->width_of[i]);
645 fnt->width_of[0] = maxwidth;
647 if(fnt == FONT_CONSOLE)
648 con_linewidth = -1; // rewrap console in next frame
651 static dp_font_t *FindFont(const char *title)
654 for(i = 0; i < MAX_FONTS; ++i)
655 if(!strcmp(dp_fonts[i].title, title))
660 static void LoadFont_f(void)
666 Con_Printf("Available font commands:\n");
667 for(i = 0; i < MAX_FONTS; ++i)
668 Con_Printf(" loadfont %s gfx/tgafile\n", dp_fonts[i].title);
671 f = FindFont(Cmd_Argv(1));
674 Con_Printf("font function not found\n");
677 LoadFont(true, (Cmd_Argc() < 3) ? "gfx/conchars" : Cmd_Argv(2), f);
685 static void gl_draw_start(void)
688 drawtexturepool = R_AllocTexturePool();
691 memset(cachepichash, 0, sizeof(cachepichash));
693 for(i = 0; i < MAX_FONTS; ++i)
694 LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i]);
696 // draw the loading screen so people have something to see in the newly opened window
697 SCR_UpdateLoadingScreen(true);
700 static void gl_draw_shutdown(void)
702 R_FreeTexturePool(&drawtexturepool);
705 memset(cachepichash, 0, sizeof(cachepichash));
708 static void gl_draw_newmap(void)
712 void GL_Draw_Init (void)
715 Cvar_RegisterVariable(&r_textshadow);
716 Cvar_RegisterVariable(&r_textbrightness);
717 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
718 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
720 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
721 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
722 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
723 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
724 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
725 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
726 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
727 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
728 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
729 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
730 if(!FONT_USER[i].title[0])
731 dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++);
734 static void _DrawQ_Setup(void)
736 if (r_refdef.draw2dstage)
738 r_refdef.draw2dstage = true;
740 qglViewport(r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
741 GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
742 GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
743 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
744 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
745 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
746 R_Mesh_Matrix(&identitymatrix);
750 GL_PolygonOffset(0, 0);
754 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
756 if (gl_support_fragment_shader)
758 qglUseProgramObjectARB(0);CHECKGLERROR
762 static void _DrawQ_ProcessDrawFlag(int flags)
766 if(flags == DRAWFLAG_ADDITIVE)
767 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
768 else if(flags == DRAWFLAG_MODULATE)
769 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
770 else if(flags == DRAWFLAG_2XMODULATE)
771 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
773 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
776 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
780 _DrawQ_ProcessDrawFlag(flags);
781 GL_Color(red, green, blue, alpha);
783 R_Mesh_VertexPointer(floats, 0, 0);
784 R_Mesh_ColorPointer(NULL, 0, 0);
785 R_Mesh_ResetTextureState();
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();
834 floats[2] = floats[5] = floats[8] = floats[11] = 0;
835 floats[0] = floats[9] = x;
836 floats[1] = floats[4] = y;
837 floats[3] = floats[6] = x + width;
838 floats[7] = floats[10] = y + height;
840 R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
843 // color tag printing
844 static vec4_t string_colors[] =
847 // LordHavoc: why on earth is cyan before magenta in Quake3?
848 // LordHavoc: note: Doom3 uses white for [0] and [7]
849 {0.0, 0.0, 0.0, 1.0}, // black
850 {1.0, 0.0, 0.0, 1.0}, // red
851 {0.0, 1.0, 0.0, 1.0}, // green
852 {1.0, 1.0, 0.0, 1.0}, // yellow
853 {0.0, 0.0, 1.0, 1.0}, // blue
854 {0.0, 1.0, 1.0, 1.0}, // cyan
855 {1.0, 0.0, 1.0, 1.0}, // magenta
856 {1.0, 1.0, 1.0, 1.0}, // white
857 // [515]'s BX_COLOREDTEXT extension
858 {1.0, 1.0, 1.0, 0.5}, // half transparent
859 {0.5, 0.5, 0.5, 1.0} // half brightness
860 // Black's color table
861 //{1.0, 1.0, 1.0, 1.0},
862 //{1.0, 0.0, 0.0, 1.0},
863 //{0.0, 1.0, 0.0, 1.0},
864 //{0.0, 0.0, 1.0, 1.0},
865 //{1.0, 1.0, 0.0, 1.0},
866 //{0.0, 1.0, 1.0, 1.0},
867 //{1.0, 0.0, 1.0, 1.0},
868 //{0.1, 0.1, 0.1, 1.0}
871 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
873 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
875 float v = r_textbrightness.value;
876 Vector4Copy(string_colors[colorindex], color);
877 Vector4Set(color, (color[0] * (1-v) + v) * r, (color[1] * (1-v) + v) * g, (color[2] * (1-v) + v) * b, color[3] * a);
880 float shadowalpha = color[0]+color[1]+color[2] * 0.8;
881 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
885 float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
887 int num, colorindex = STRING_COLOR_DEFAULT;
894 if (!outcolor || *outcolor == -1)
895 colorindex = STRING_COLOR_DEFAULT;
897 colorindex = *outcolor;
899 for (i = 0;i < *maxlen && text[i];i++)
903 if(x + fnt->width_of[' '] > maxwidth)
904 break; // oops, can't draw this
905 x += fnt->width_of[' '];
908 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen)
910 if (text[i+1] == STRING_COLOR_TAG)
914 else if (text[i+1] >= '0' && text[i+1] <= '9')
916 colorindex = text[i+1] - '0';
921 num = (unsigned char) text[i];
922 if(x + fnt->width_of[num] > maxwidth)
923 break; // oops, can't draw this
924 x += fnt->width_of[num];
930 *outcolor = colorindex;
935 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)
937 int num, shadow, colorindex = STRING_COLOR_DEFAULT;
939 float x = startx, y, s, t, u, v;
943 float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
944 float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
945 float color4f[QUADELEMENTS_MAXQUADS*4*4];
950 // when basealpha == 0, skip as much as possible (just return width)
953 _DrawQ_ProcessDrawFlag(flags);
955 R_Mesh_ColorPointer(color4f, 0, 0);
956 R_Mesh_ResetTextureState();
957 R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
958 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
959 R_Mesh_VertexPointer(vertex3f, 0, 0);
967 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
969 if (!outcolor || *outcolor == -1)
970 colorindex = STRING_COLOR_DEFAULT;
972 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];
1007 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1008 s = (num & 15)*0.0625f + (0.5f / 256.0f);
1009 t = (num >> 4)*0.0625f + (0.5f / 256.0f);
1010 u = 0.0625f - (1.0f / 256.0f);
1011 v = 0.0625f - (1.0f / 256.0f);
1012 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1013 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1014 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1015 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1016 at[ 0] = s ;at[ 1] = t ;
1017 at[ 2] = s+u;at[ 3] = t ;
1018 at[ 4] = s+u;at[ 5] = t+v;
1019 at[ 6] = s ;at[ 7] = t+v;
1020 av[ 0] = x ;av[ 1] = y ;av[ 2] = 10;
1021 av[ 3] = x+w;av[ 4] = y ;av[ 5] = 10;
1022 av[ 6] = x+w;av[ 7] = y+h;av[ 8] = 10;
1023 av[ 9] = x ;av[10] = y+h;av[11] = 10;
1028 if (batchcount >= QUADELEMENTS_MAXQUADS)
1030 if (basealpha >= (1.0f / 255.0f))
1032 GL_LockArrays(0, batchcount * 4);
1033 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
1034 GL_LockArrays(0, 0);
1042 x += fnt->width_of[num] * w;
1049 GL_LockArrays(0, batchcount * 4);
1050 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
1051 GL_LockArrays(0, 0);
1056 *outcolor = colorindex;
1058 // note: this relies on the proper text (not shadow) being drawn last
1062 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)
1064 return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]);
1067 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1069 return DrawQ_TextWidth_Font_UntilWidth_TrackColors(text, maxlen, NULL, ignorecolorcodes, fnt, maxWidth);
1072 float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt)
1074 return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, ignorecolorcodes, fnt, 1000000000);
1079 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1081 int color, numchars = 0;
1082 char *outputend2c = output2c + maxoutchars - 2;
1083 if (!outcolor || *outcolor == -1)
1084 color = STRING_COLOR_DEFAULT;
1088 maxreadchars = 1<<30;
1089 textend = text + maxreadchars;
1090 while (text != textend && *text)
1092 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1094 if (text[1] == STRING_COLOR_TAG)
1096 else if (text[1] >= '0' && text[1] <= '9')
1098 color = text[1] - '0';
1103 if (output2c >= outputend2c)
1105 *output2c++ = *text++;
1106 *output2c++ = color;
1109 output2c[0] = output2c[1] = 0;
1116 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)
1120 _DrawQ_ProcessDrawFlag(flags);
1122 R_Mesh_VertexPointer(floats, 0, 0);
1123 R_Mesh_ColorPointer(floats + 20, 0, 0);
1124 R_Mesh_ResetTextureState();
1130 height = pic->height;
1131 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1132 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1133 floats[12] = s1;floats[13] = t1;
1134 floats[14] = s2;floats[15] = t2;
1135 floats[16] = s4;floats[17] = t4;
1136 floats[18] = s3;floats[19] = t3;
1139 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1140 floats[0] = floats[9] = x;
1141 floats[1] = floats[4] = y;
1142 floats[3] = floats[6] = x + width;
1143 floats[7] = floats[10] = y + height;
1144 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1145 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1146 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1147 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1149 R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
1152 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1154 _DrawQ_ProcessDrawFlag(flags);
1156 R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1157 R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1158 R_Mesh_ResetTextureState();
1159 R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
1160 R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1162 GL_LockArrays(0, mesh->num_vertices);
1163 R_Mesh_Draw(0, mesh->num_vertices, mesh->num_triangles, mesh->data_element3i, 0, 0);
1164 GL_LockArrays(0, 0);
1167 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1171 _DrawQ_ProcessDrawFlag(flags);
1175 qglBegin(GL_LINE_LOOP);
1176 for (num = 0;num < mesh->num_vertices;num++)
1178 if (mesh->data_color4f)
1179 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]);
1180 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1186 //[515]: this is old, delete
1187 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1189 _DrawQ_ProcessDrawFlag(flags);
1192 qglLineWidth(width);CHECKGLERROR
1194 GL_Color(r,g,b,alpha);
1197 qglVertex2f(x1, y1);
1198 qglVertex2f(x2, y2);
1203 void DrawQ_SetClipArea(float x, float y, float width, float height)
1207 // We have to convert the con coords into real coords
1208 // OGL uses top to bottom
1209 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)));
1211 GL_ScissorTest(true);
1214 void DrawQ_ResetClipArea(void)
1217 GL_ScissorTest(false);
1220 void DrawQ_Finish(void)
1222 r_refdef.draw2dstage = false;
1225 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1226 void R_DrawGamma(void)
1229 if (!vid_usinghwgamma)
1231 // all the blends ignore depth
1232 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1233 R_Mesh_ColorPointer(NULL, 0, 0);
1234 R_Mesh_ResetTextureState();
1236 GL_DepthRange(0, 1);
1237 GL_PolygonOffset(0, 0);
1238 GL_DepthTest(false);
1239 if (v_color_enable.integer)
1241 c[0] = v_color_white_r.value;
1242 c[1] = v_color_white_g.value;
1243 c[2] = v_color_white_b.value;
1246 c[0] = c[1] = c[2] = v_contrast.value;
1247 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1249 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1250 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1252 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1253 R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);
1254 VectorScale(c, 0.5, c);
1257 if (v_color_enable.integer)
1259 c[0] = v_color_black_r.value;
1260 c[1] = v_color_black_g.value;
1261 c[2] = v_color_black_b.value;
1264 c[0] = c[1] = c[2] = v_brightness.value;
1265 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1267 GL_BlendFunc(GL_ONE, GL_ONE);
1268 GL_Color(c[0], c[1], c[2], 1);
1269 R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);