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, 0 makes them all black)"};
34 extern cvar_t v_glslgamma;
36 //=============================================================================
37 /* Support Routines */
39 #define FONT_FILESIZE 13468
40 #define MAX_CACHED_PICS 1024
41 #define CACHEPICHASHSIZE 256
42 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
43 static cachepic_t cachepics[MAX_CACHED_PICS];
44 static int numcachepics;
46 static rtexturepool_t *drawtexturepool;
48 static const unsigned char concharimage[FONT_FILESIZE] =
53 static rtexture_t *draw_generateconchars(void)
56 unsigned char buffer[65536][4], *data = NULL;
59 data = LoadTGA_BGRA (concharimage, FONT_FILESIZE);
61 for (i = 0;i < 8192;i++)
63 random = lhrandom (0.0,1.0);
64 buffer[i][2] = 83 + (unsigned char)(random * 64);
65 buffer[i][1] = 71 + (unsigned char)(random * 32);
66 buffer[i][0] = 23 + (unsigned char)(random * 16);
67 buffer[i][3] = data[i*4+0];
70 for (i = 8192;i < 32768;i++)
72 random = lhrandom (0.0,1.0);
73 buffer[i][2] = 95 + (unsigned char)(random * 64);
74 buffer[i][1] = 95 + (unsigned char)(random * 64);
75 buffer[i][0] = 95 + (unsigned char)(random * 64);
76 buffer[i][3] = data[i*4+0];
79 for (i = 32768;i < 40960;i++)
81 random = lhrandom (0.0,1.0);
82 buffer[i][2] = 83 + (unsigned char)(random * 64);
83 buffer[i][1] = 71 + (unsigned char)(random * 32);
84 buffer[i][0] = 23 + (unsigned char)(random * 16);
85 buffer[i][3] = data[i*4+0];
88 for (i = 40960;i < 65536;i++)
90 random = lhrandom (0.0,1.0);
91 buffer[i][2] = 96 + (unsigned char)(random * 64);
92 buffer[i][1] = 43 + (unsigned char)(random * 32);
93 buffer[i][0] = 27 + (unsigned char)(random * 32);
94 buffer[i][3] = data[i*4+0];
98 Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, &buffer[0][0]);
102 return R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, &buffer[0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
105 static rtexture_t *draw_generateditherpattern(void)
108 unsigned char pixels[8][8];
109 for (y = 0;y < 8;y++)
110 for (x = 0;x < 8;x++)
111 pixels[y][x] = ((x^y) & 4) ? 254 : 0;
112 return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST | TEXF_PRECACHE, palette_bgra_transparent);
115 typedef struct embeddedpic_s
124 static const embeddedpic_t embeddedpics[] =
127 "gfx/prydoncursor001", 16, 16,
146 "ui/mousepointer", 16, 16,
165 "gfx/crosshair1", 16, 16,
184 "gfx/crosshair2", 16, 16,
203 "gfx/crosshair3", 16, 16,
222 "gfx/crosshair4", 16, 16,
241 "gfx/crosshair5", 8, 8,
252 "gfx/crosshair6", 2, 2,
257 "gfx/crosshair7", 16, 16,
276 "gfx/editlights/cursor", 16, 16,
295 "gfx/editlights/light", 16, 16,
314 "gfx/editlights/noshadow", 16, 16,
333 "gfx/editlights/selection", 16, 16,
352 "gfx/editlights/cubemaplight", 16, 16,
371 "gfx/editlights/cubemapnoshadowlight", 16, 16,
392 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
394 const embeddedpic_t *p;
395 for (p = embeddedpics;p->name;p++)
396 if (!strcmp(name, p->name))
397 return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA | TEXF_PRECACHE, palette_bgra_embeddedpic);
398 if (!strcmp(name, "gfx/conchars"))
399 return draw_generateconchars();
400 if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
401 return draw_generateditherpattern();
403 Con_Printf("Draw_CachePic: failed to load %s\n", name);
404 return r_texture_notexture;
413 // FIXME: move this to client somehow
414 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
420 unsigned char *lmpdata;
421 char lmpname[MAX_QPATH];
423 // check whether the picture has already been cached
424 crc = CRC_Block((unsigned char *)path, strlen(path));
425 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
426 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
427 if (!strcmp (path, pic->name))
430 if (numcachepics == MAX_CACHED_PICS)
432 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
433 // FIXME: support NULL in callers?
434 return cachepics; // return the first one
436 pic = cachepics + (numcachepics++);
437 strlcpy (pic->name, path, sizeof(pic->name));
439 pic->chain = cachepichash[hashkey];
440 cachepichash[hashkey] = pic;
442 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
443 pic->tex = CL_GetDynTexture( path );
444 // if so, set the width/height, too
446 pic->width = R_TextureWidth(pic->tex);
447 pic->height = R_TextureHeight(pic->tex);
448 // we're done now (early-out)
453 if (!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
454 flags |= TEXF_PRECACHE;
455 if (strcmp(path, "gfx/colorcontrol/ditherpattern"))
457 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer)
458 flags |= TEXF_COMPRESS;
460 // load a high quality image from disk if possible
461 pic->tex = loadtextureimage(drawtexturepool, path, false, flags, true);
462 if (pic->tex == NULL && !strncmp(path, "gfx/", 4))
464 // compatibility with older versions which did not require gfx/ prefix
465 pic->tex = loadtextureimage(drawtexturepool, path + 4, false, flags, true);
467 // if a high quality image was loaded, set the pic's size to match it, just
468 // in case there's no low quality version to get the size from
471 pic->width = R_TextureWidth(pic->tex);
472 pic->height = R_TextureHeight(pic->tex);
475 // now read the low quality version (wad or lmp file), and take the pic
476 // size from that even if we don't upload the texture, this way the pics
477 // show up the right size in the menu even if they were replaced with
478 // higher or lower resolution versions
479 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
480 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
482 if (developer_loading.integer)
483 Con_Printf("loading lump \"%s\"\n", path);
487 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
488 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
489 // if no high quality replacement image was found, upload the original low quality texture
491 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
495 else if ((lmpdata = W_GetLumpName (path + 4)))
497 if (developer_loading.integer)
498 Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
500 if (!strcmp(path, "gfx/conchars"))
502 // conchars is a raw image and with color 0 as transparent instead of 255
505 // if no high quality replacement image was found, upload the original low quality texture
507 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_font);
511 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
512 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
513 // if no high quality replacement image was found, upload the original low quality texture
515 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
519 // if it's not found on disk, generate an image
520 if (pic->tex == NULL)
522 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
523 pic->width = R_TextureWidth(pic->tex);
524 pic->height = R_TextureHeight(pic->tex);
530 cachepic_t *Draw_CachePic (const char *path)
532 return Draw_CachePic_Flags (path, 0);
535 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
540 crc = CRC_Block((unsigned char *)picname, strlen(picname));
541 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
542 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
543 if (!strcmp (picname, pic->name))
548 if (pic->tex && pic->width == width && pic->height == height)
550 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
558 if (numcachepics == MAX_CACHED_PICS)
560 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
561 // FIXME: support NULL in callers?
562 return cachepics; // return the first one
564 pic = cachepics + (numcachepics++);
565 strlcpy (pic->name, picname, sizeof(pic->name));
567 pic->chain = cachepichash[hashkey];
568 cachepichash[hashkey] = pic;
573 pic->height = height;
575 R_FreeTexture(pic->tex);
576 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, alpha ? TEXF_ALPHA : 0, NULL);
580 void Draw_FreePic(const char *picname)
585 // this doesn't really free the pic, but does free it's texture
586 crc = CRC_Block((unsigned char *)picname, strlen(picname));
587 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
588 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
590 if (!strcmp (picname, pic->name) && pic->tex)
592 R_FreeTexture(pic->tex);
600 extern int con_linewidth; // to force rewrapping
601 static void LoadFont(qboolean override, const char *name, dp_font_t *fnt)
604 float maxwidth, scale;
605 char widthfile[MAX_QPATH];
607 fs_offset_t widthbufsize;
609 if(override || !fnt->texpath[0])
610 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
612 if(drawtexturepool == NULL)
613 return; // before gl_draw_start, so will be loaded later
615 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
616 if(fnt->tex == r_texture_notexture)
618 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
619 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
622 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
624 // unspecified width == 1 (base width)
625 for(i = 1; i < 256; ++i)
626 fnt->width_of[i] = 1;
629 // FIXME load "name.width", if it fails, fill all with 1
630 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
632 float extraspacing = 0;
633 const char *p = widthbuf;
638 if(!COM_ParseToken_Simple(&p, false, false))
641 if(!strcmp(com_token, "extraspacing"))
643 if(!COM_ParseToken_Simple(&p, false, false))
645 extraspacing = atof(com_token);
647 else if(!strcmp(com_token, "scale"))
649 if(!COM_ParseToken_Simple(&p, false, false))
651 scale = atof(com_token);
654 fnt->width_of[ch++] = atof(com_token) + extraspacing;
660 maxwidth = fnt->width_of[1];
661 for(i = 2; i < 256; ++i)
662 maxwidth = max(maxwidth, fnt->width_of[i]);
663 fnt->maxwidth = maxwidth;
665 // fix up maxwidth for overlap
666 fnt->maxwidth *= scale;
669 if(fnt == FONT_CONSOLE)
670 con_linewidth = -1; // rewrap console in next frame
673 static dp_font_t *FindFont(const char *title)
676 for(i = 0; i < MAX_FONTS; ++i)
677 if(!strcmp(dp_fonts[i].title, title))
682 static void LoadFont_f(void)
688 Con_Printf("Available font commands:\n");
689 for(i = 0; i < MAX_FONTS; ++i)
690 Con_Printf(" loadfont %s gfx/tgafile\n", dp_fonts[i].title);
693 f = FindFont(Cmd_Argv(1));
696 Con_Printf("font function not found\n");
699 LoadFont(true, (Cmd_Argc() < 3) ? "gfx/conchars" : Cmd_Argv(2), f);
707 static void gl_draw_start(void)
710 drawtexturepool = R_AllocTexturePool();
713 memset(cachepichash, 0, sizeof(cachepichash));
715 for(i = 0; i < MAX_FONTS; ++i)
716 LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i]);
718 // draw the loading screen so people have something to see in the newly opened window
719 SCR_UpdateLoadingScreen(true);
722 static void gl_draw_shutdown(void)
724 R_FreeTexturePool(&drawtexturepool);
727 memset(cachepichash, 0, sizeof(cachepichash));
730 static void gl_draw_newmap(void)
734 void GL_Draw_Init (void)
737 Cvar_RegisterVariable(&r_textshadow);
738 Cvar_RegisterVariable(&r_textbrightness);
739 Cvar_RegisterVariable(&r_textcontrast);
740 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
741 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
743 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
744 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
745 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
746 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
747 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
748 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
749 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
750 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
751 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
752 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
753 if(!FONT_USER[i].title[0])
754 dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++);
757 void _DrawQ_Setup(void)
759 if (r_refdef.draw2dstage)
761 r_refdef.draw2dstage = true;
763 qglViewport(r_refdef.view.x, vid.height - (r_refdef.view.y + r_refdef.view.height), r_refdef.view.width, r_refdef.view.height);CHECKGLERROR
764 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
765 GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
766 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
767 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
768 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
769 R_Mesh_Matrix(&identitymatrix);
773 GL_PolygonOffset(0, 0);
777 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
779 R_SetupGenericShader(true);
782 static void _DrawQ_ProcessDrawFlag(int flags)
786 if(flags == DRAWFLAG_ADDITIVE)
787 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
788 else if(flags == DRAWFLAG_MODULATE)
789 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
790 else if(flags == DRAWFLAG_2XMODULATE)
791 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
793 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
796 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
800 _DrawQ_ProcessDrawFlag(flags);
801 GL_Color(red, green, blue, alpha);
803 R_Mesh_VertexPointer(floats, 0, 0);
804 R_Mesh_ColorPointer(NULL, 0, 0);
805 R_Mesh_ResetTextureState();
806 R_SetupGenericShader(pic != NULL);
812 height = pic->height;
813 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
814 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
817 floats[12] = 0.0f;floats[13] = 0.0f;
818 floats[14] = 1.0f;floats[15] = 0.0f;
819 floats[16] = 1.0f;floats[17] = 1.0f;
820 floats[18] = 0.0f;floats[19] = 1.0f;
822 // AK07: lets be texel correct on the corners
824 float horz_offset = 0.5f / pic->width;
825 float vert_offset = 0.5f / pic->height;
827 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
828 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
829 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
830 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
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, 0, 2, NULL, polygonelements, 0, 0);
844 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
848 _DrawQ_ProcessDrawFlag(flags);
849 GL_Color(red, green, blue, alpha);
851 R_Mesh_VertexPointer(floats, 0, 0);
852 R_Mesh_ColorPointer(NULL, 0, 0);
853 R_Mesh_ResetTextureState();
854 R_SetupGenericShader(false);
856 floats[2] = floats[5] = floats[8] = floats[11] = 0;
857 floats[0] = floats[9] = x;
858 floats[1] = floats[4] = y;
859 floats[3] = floats[6] = x + width;
860 floats[7] = floats[10] = y + height;
862 R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
865 // color tag printing
866 static const vec4_t string_colors[] =
869 // LordHavoc: why on earth is cyan before magenta in Quake3?
870 // LordHavoc: note: Doom3 uses white for [0] and [7]
871 {0.0, 0.0, 0.0, 1.0}, // black
872 {1.0, 0.0, 0.0, 1.0}, // red
873 {0.0, 1.0, 0.0, 1.0}, // green
874 {1.0, 1.0, 0.0, 1.0}, // yellow
875 {0.0, 0.0, 1.0, 1.0}, // blue
876 {0.0, 1.0, 1.0, 1.0}, // cyan
877 {1.0, 0.0, 1.0, 1.0}, // magenta
878 {1.0, 1.0, 1.0, 1.0}, // white
879 // [515]'s BX_COLOREDTEXT extension
880 {1.0, 1.0, 1.0, 0.5}, // half transparent
881 {0.5, 0.5, 0.5, 1.0} // half brightness
882 // Black's color table
883 //{1.0, 1.0, 1.0, 1.0},
884 //{1.0, 0.0, 0.0, 1.0},
885 //{0.0, 1.0, 0.0, 1.0},
886 //{0.0, 0.0, 1.0, 1.0},
887 //{1.0, 1.0, 0.0, 1.0},
888 //{0.0, 1.0, 1.0, 1.0},
889 //{1.0, 0.0, 1.0, 1.0},
890 //{0.1, 0.1, 0.1, 1.0}
893 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
895 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
897 float C = r_textcontrast.value;
898 float B = r_textbrightness.value;
899 Vector4Copy(string_colors[colorindex], color);
900 Vector4Set(color, (color[0] * C + B) * r, (color[1] * C + B) * g, (color[2] * C + B) * b, color[3] * a);
903 float shadowalpha = color[0]+color[1]+color[2] * 0.8;
904 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
908 float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
910 int num, colorindex = STRING_COLOR_DEFAULT;
917 if (!outcolor || *outcolor == -1)
918 colorindex = STRING_COLOR_DEFAULT;
920 colorindex = *outcolor;
922 maxwidth /= fnt->scale;
924 for (i = 0;i < *maxlen && text[i];i++)
928 if(x + fnt->width_of[' '] > maxwidth)
929 break; // oops, can't draw this
930 x += fnt->width_of[' '];
933 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen)
935 if (text[i+1] == STRING_COLOR_TAG)
939 else if (text[i+1] >= '0' && text[i+1] <= '9')
941 colorindex = text[i+1] - '0';
946 num = (unsigned char) text[i];
947 if(x + fnt->width_of[num] > maxwidth)
948 break; // oops, can't draw this
949 x += fnt->width_of[num];
955 *outcolor = colorindex;
957 return x * fnt->scale;
960 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)
962 int num, shadow, colorindex = STRING_COLOR_DEFAULT;
964 float x = startx, y, s, t, u, v, thisw;
968 float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
969 float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
970 float color4f[QUADELEMENTS_MAXQUADS*4*4];
973 tw = R_TextureWidth(fnt->tex);
974 th = R_TextureHeight(fnt->tex);
976 starty -= (fnt->scale - 1) * h * 0.5; // center
983 _DrawQ_ProcessDrawFlag(flags);
985 R_Mesh_ColorPointer(color4f, 0, 0);
986 R_Mesh_ResetTextureState();
987 R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
988 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
989 R_Mesh_VertexPointer(vertex3f, 0, 0);
990 R_SetupGenericShader(true);
997 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
999 if (!outcolor || *outcolor == -1)
1000 colorindex = STRING_COLOR_DEFAULT;
1002 colorindex = *outcolor;
1003 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
1009 x += r_textshadow.value;
1010 y += r_textshadow.value;
1012 for (i = 0;i < maxlen && text[i];i++)
1016 x += fnt->width_of[' '] * w;
1019 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen)
1021 if (text[i+1] == STRING_COLOR_TAG)
1025 else if (text[i+1] >= '0' && text[i+1] <= '9')
1027 colorindex = text[i+1] - '0';
1028 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
1033 num = (unsigned char) text[i];
1034 thisw = fnt->width_of[num];
1035 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1036 s = (num & 15)*0.0625f + (0.5f / tw);
1037 t = (num >> 4)*0.0625f + (0.5f / th);
1038 u = 0.0625f * thisw - (1.0f / tw);
1039 v = 0.0625f - (1.0f / th);
1040 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1041 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1042 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1043 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1044 at[ 0] = s ;at[ 1] = t ;
1045 at[ 2] = s+u;at[ 3] = t ;
1046 at[ 4] = s+u;at[ 5] = t+v;
1047 at[ 6] = s ;at[ 7] = t+v;
1048 av[ 0] = x ;av[ 1] = y ;av[ 2] = 10;
1049 av[ 3] = x+w*thisw;av[ 4] = y ;av[ 5] = 10;
1050 av[ 6] = x+w*thisw;av[ 7] = y+h;av[ 8] = 10;
1051 av[ 9] = x ;av[10] = y+h;av[11] = 10;
1056 if (batchcount >= QUADELEMENTS_MAXQUADS)
1058 GL_LockArrays(0, batchcount * 4);
1059 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, NULL, quadelements, 0, 0);
1060 GL_LockArrays(0, 0);
1071 GL_LockArrays(0, batchcount * 4);
1072 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, NULL, quadelements, 0, 0);
1073 GL_LockArrays(0, 0);
1077 *outcolor = colorindex;
1079 // note: this relies on the proper text (not shadow) being drawn last
1083 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)
1085 return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]);
1088 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1090 return DrawQ_TextWidth_Font_UntilWidth_TrackColors(text, maxlen, NULL, ignorecolorcodes, fnt, maxWidth);
1093 float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt)
1095 return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, ignorecolorcodes, fnt, 1000000000);
1100 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1102 int color, numchars = 0;
1103 char *outputend2c = output2c + maxoutchars - 2;
1104 if (!outcolor || *outcolor == -1)
1105 color = STRING_COLOR_DEFAULT;
1109 maxreadchars = 1<<30;
1110 textend = text + maxreadchars;
1111 while (text != textend && *text)
1113 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1115 if (text[1] == STRING_COLOR_TAG)
1117 else if (text[1] >= '0' && text[1] <= '9')
1119 color = text[1] - '0';
1124 if (output2c >= outputend2c)
1126 *output2c++ = *text++;
1127 *output2c++ = color;
1130 output2c[0] = output2c[1] = 0;
1137 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)
1141 _DrawQ_ProcessDrawFlag(flags);
1143 R_Mesh_VertexPointer(floats, 0, 0);
1144 R_Mesh_ColorPointer(floats + 20, 0, 0);
1145 R_Mesh_ResetTextureState();
1146 R_SetupGenericShader(pic != NULL);
1152 height = pic->height;
1153 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1154 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1155 floats[12] = s1;floats[13] = t1;
1156 floats[14] = s2;floats[15] = t2;
1157 floats[16] = s4;floats[17] = t4;
1158 floats[18] = s3;floats[19] = t3;
1161 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1162 floats[0] = floats[9] = x;
1163 floats[1] = floats[4] = y;
1164 floats[3] = floats[6] = x + width;
1165 floats[7] = floats[10] = y + height;
1166 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1167 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1168 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1169 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1171 R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
1174 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1176 _DrawQ_ProcessDrawFlag(flags);
1178 R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1179 R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1180 R_Mesh_ResetTextureState();
1181 R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
1182 R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1183 R_SetupGenericShader(mesh->texture != NULL);
1185 GL_LockArrays(0, mesh->num_vertices);
1186 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, NULL, mesh->data_element3s, 0, 0);
1187 GL_LockArrays(0, 0);
1190 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1194 _DrawQ_ProcessDrawFlag(flags);
1198 qglBegin(GL_LINE_LOOP);
1199 for (num = 0;num < mesh->num_vertices;num++)
1201 if (mesh->data_color4f)
1202 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]);
1203 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1209 //[515]: this is old, delete
1210 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1212 _DrawQ_ProcessDrawFlag(flags);
1214 R_SetupGenericShader(false);
1217 qglLineWidth(width);CHECKGLERROR
1219 GL_Color(r,g,b,alpha);
1222 qglVertex2f(x1, y1);
1223 qglVertex2f(x2, y2);
1228 void DrawQ_SetClipArea(float x, float y, float width, float height)
1232 // We have to convert the con coords into real coords
1233 // OGL uses top to bottom
1234 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)));
1236 GL_ScissorTest(true);
1239 void DrawQ_ResetClipArea(void)
1242 GL_ScissorTest(false);
1245 void DrawQ_Finish(void)
1247 r_refdef.draw2dstage = false;
1250 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1251 void R_DrawGamma(void)
1254 if (!vid_usinghwgamma && !(r_glsl.integer && v_glslgamma.integer))
1256 // all the blends ignore depth
1257 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1258 R_Mesh_ColorPointer(NULL, 0, 0);
1259 R_Mesh_ResetTextureState();
1260 R_SetupGenericShader(false);
1262 GL_DepthRange(0, 1);
1263 GL_PolygonOffset(0, 0);
1264 GL_DepthTest(false);
1265 if (v_color_enable.integer)
1267 c[0] = v_color_white_r.value;
1268 c[1] = v_color_white_g.value;
1269 c[2] = v_color_white_b.value;
1272 c[0] = c[1] = c[2] = v_contrast.value;
1273 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1275 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1276 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1278 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1279 R_Mesh_Draw(0, 3, 0, 1, NULL, polygonelements, 0, 0);
1280 VectorScale(c, 0.5, c);
1283 if (v_color_enable.integer)
1285 c[0] = v_color_black_r.value;
1286 c[1] = v_color_black_g.value;
1287 c[2] = v_color_black_b.value;
1290 c[0] = c[1] = c[2] = v_brightness.value;
1291 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1293 GL_BlendFunc(GL_ONE, GL_ONE);
1294 GL_Color(c[0], c[1], c[2], 1);
1295 R_Mesh_Draw(0, 3, 0, 1, NULL, polygonelements, 0, 0);