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 const 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 const 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)
391 const embeddedpic_t *p;
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 const 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 _DrawQ_ProcessDrawFlag(flags);
952 R_Mesh_ColorPointer(color4f, 0, 0);
953 R_Mesh_ResetTextureState();
954 R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
955 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
956 R_Mesh_VertexPointer(vertex3f, 0, 0);
963 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
965 if (!outcolor || *outcolor == -1)
966 colorindex = STRING_COLOR_DEFAULT;
968 colorindex = *outcolor;
969 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
975 x += r_textshadow.value;
976 y += r_textshadow.value;
978 for (i = 0;i < maxlen && text[i];i++)
982 x += fnt->width_of[' '] * w;
985 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen)
987 if (text[i+1] == STRING_COLOR_TAG)
991 else if (text[i+1] >= '0' && text[i+1] <= '9')
993 colorindex = text[i+1] - '0';
994 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
999 num = (unsigned char) text[i];
1000 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1001 s = (num & 15)*0.0625f + (0.5f / 256.0f);
1002 t = (num >> 4)*0.0625f + (0.5f / 256.0f);
1003 u = 0.0625f - (1.0f / 256.0f);
1004 v = 0.0625f - (1.0f / 256.0f);
1005 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1006 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1007 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1008 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1009 at[ 0] = s ;at[ 1] = t ;
1010 at[ 2] = s+u;at[ 3] = t ;
1011 at[ 4] = s+u;at[ 5] = t+v;
1012 at[ 6] = s ;at[ 7] = t+v;
1013 av[ 0] = x ;av[ 1] = y ;av[ 2] = 10;
1014 av[ 3] = x+w;av[ 4] = y ;av[ 5] = 10;
1015 av[ 6] = x+w;av[ 7] = y+h;av[ 8] = 10;
1016 av[ 9] = x ;av[10] = y+h;av[11] = 10;
1021 if (batchcount >= QUADELEMENTS_MAXQUADS)
1023 GL_LockArrays(0, batchcount * 4);
1024 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
1025 GL_LockArrays(0, 0);
1031 x += fnt->width_of[num] * w;
1036 GL_LockArrays(0, batchcount * 4);
1037 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
1038 GL_LockArrays(0, 0);
1042 *outcolor = colorindex;
1044 // note: this relies on the proper text (not shadow) being drawn last
1048 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)
1050 return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]);
1053 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1055 return DrawQ_TextWidth_Font_UntilWidth_TrackColors(text, maxlen, NULL, ignorecolorcodes, fnt, maxWidth);
1058 float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt)
1060 return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, ignorecolorcodes, fnt, 1000000000);
1065 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1067 int color, numchars = 0;
1068 char *outputend2c = output2c + maxoutchars - 2;
1069 if (!outcolor || *outcolor == -1)
1070 color = STRING_COLOR_DEFAULT;
1074 maxreadchars = 1<<30;
1075 textend = text + maxreadchars;
1076 while (text != textend && *text)
1078 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1080 if (text[1] == STRING_COLOR_TAG)
1082 else if (text[1] >= '0' && text[1] <= '9')
1084 color = text[1] - '0';
1089 if (output2c >= outputend2c)
1091 *output2c++ = *text++;
1092 *output2c++ = color;
1095 output2c[0] = output2c[1] = 0;
1102 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)
1106 _DrawQ_ProcessDrawFlag(flags);
1108 R_Mesh_VertexPointer(floats, 0, 0);
1109 R_Mesh_ColorPointer(floats + 20, 0, 0);
1110 R_Mesh_ResetTextureState();
1116 height = pic->height;
1117 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1118 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1119 floats[12] = s1;floats[13] = t1;
1120 floats[14] = s2;floats[15] = t2;
1121 floats[16] = s4;floats[17] = t4;
1122 floats[18] = s3;floats[19] = t3;
1125 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1126 floats[0] = floats[9] = x;
1127 floats[1] = floats[4] = y;
1128 floats[3] = floats[6] = x + width;
1129 floats[7] = floats[10] = y + height;
1130 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1131 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1132 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1133 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1135 R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
1138 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1140 _DrawQ_ProcessDrawFlag(flags);
1142 R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1143 R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1144 R_Mesh_ResetTextureState();
1145 R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
1146 R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1148 GL_LockArrays(0, mesh->num_vertices);
1149 R_Mesh_Draw(0, mesh->num_vertices, mesh->num_triangles, mesh->data_element3i, 0, 0);
1150 GL_LockArrays(0, 0);
1153 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1157 _DrawQ_ProcessDrawFlag(flags);
1161 qglBegin(GL_LINE_LOOP);
1162 for (num = 0;num < mesh->num_vertices;num++)
1164 if (mesh->data_color4f)
1165 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]);
1166 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1172 //[515]: this is old, delete
1173 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1175 _DrawQ_ProcessDrawFlag(flags);
1178 qglLineWidth(width);CHECKGLERROR
1180 GL_Color(r,g,b,alpha);
1183 qglVertex2f(x1, y1);
1184 qglVertex2f(x2, y2);
1189 void DrawQ_SetClipArea(float x, float y, float width, float height)
1193 // We have to convert the con coords into real coords
1194 // OGL uses top to bottom
1195 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)));
1197 GL_ScissorTest(true);
1200 void DrawQ_ResetClipArea(void)
1203 GL_ScissorTest(false);
1206 void DrawQ_Finish(void)
1208 r_refdef.draw2dstage = false;
1211 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1212 void R_DrawGamma(void)
1215 if (!vid_usinghwgamma)
1217 // all the blends ignore depth
1218 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1219 R_Mesh_ColorPointer(NULL, 0, 0);
1220 R_Mesh_ResetTextureState();
1222 GL_DepthRange(0, 1);
1223 GL_PolygonOffset(0, 0);
1224 GL_DepthTest(false);
1225 if (v_color_enable.integer)
1227 c[0] = v_color_white_r.value;
1228 c[1] = v_color_white_g.value;
1229 c[2] = v_color_white_b.value;
1232 c[0] = c[1] = c[2] = v_contrast.value;
1233 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1235 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1236 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1238 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1239 R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);
1240 VectorScale(c, 0.5, c);
1243 if (v_color_enable.integer)
1245 c[0] = v_color_black_r.value;
1246 c[1] = v_color_black_g.value;
1247 c[2] = v_color_black_b.value;
1250 c[0] = c[1] = c[2] = v_brightness.value;
1251 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1253 GL_BlendFunc(GL_ONE, GL_ONE);
1254 GL_Color(c[0], c[1], c[2], 1);
1255 R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);