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_FONTS; ++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 static float DrawQ_String_Font_UntilX(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, float maxx)
887 int num, shadow, colorindex = STRING_COLOR_DEFAULT;
889 float x = startx, y, s, t, u, v;
893 float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
894 float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
895 float color4f[QUADELEMENTS_MAXQUADS*4*4];
901 // when basealpha == 0, skip as much as possible (just return width)
904 _DrawQ_ProcessDrawFlag(flags);
906 R_Mesh_ColorPointer(color4f, 0, 0);
907 R_Mesh_ResetTextureState();
908 R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
909 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
910 R_Mesh_VertexPointer(vertex3f, 0, 0);
917 checkwidth = (maxx >= startx);
919 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
921 if (!outcolor || *outcolor == -1)
922 colorindex = STRING_COLOR_DEFAULT;
924 colorindex = *outcolor;
926 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
932 x += r_textshadow.value;
933 y += r_textshadow.value;
935 for (i = 0;i < *maxlen && text[i];i++)
940 if(x + fnt->width_of[' '] * w > maxx)
941 break; // oops, can't draw this
942 x += fnt->width_of[' '] * w;
945 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen)
947 if (text[i+1] == STRING_COLOR_TAG)
951 else if (text[i+1] >= '0' && text[i+1] <= '9')
953 colorindex = text[i+1] - '0';
954 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
959 num = (unsigned char) text[i];
961 if(x + fnt->width_of[num] * w > maxx)
962 break; // oops, can't draw this
965 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
966 s = (num & 15)*0.0625f + (0.5f / 256.0f);
967 t = (num >> 4)*0.0625f + (0.5f / 256.0f);
968 u = 0.0625f - (1.0f / 256.0f);
969 v = 0.0625f - (1.0f / 256.0f);
970 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
971 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
972 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
973 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
974 at[ 0] = s ;at[ 1] = t ;
975 at[ 2] = s+u;at[ 3] = t ;
976 at[ 4] = s+u;at[ 5] = t+v;
977 at[ 6] = s ;at[ 7] = t+v;
978 av[ 0] = x ;av[ 1] = y ;av[ 2] = 10;
979 av[ 3] = x+w;av[ 4] = y ;av[ 5] = 10;
980 av[ 6] = x+w;av[ 7] = y+h;av[ 8] = 10;
981 av[ 9] = x ;av[10] = y+h;av[11] = 10;
986 if (batchcount >= QUADELEMENTS_MAXQUADS)
988 if (basealpha >= (1.0f / 255.0f))
990 GL_LockArrays(0, batchcount * 4);
991 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
1000 x += fnt->width_of[num] * w;
1004 checkwidth = 0; // we've done all we had to
1010 GL_LockArrays(0, batchcount * 4);
1011 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
1012 GL_LockArrays(0, 0);
1017 *outcolor = colorindex;
1019 // note: this relies on the proper text (not shadow) being drawn last
1023 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)
1025 return DrawQ_String_Font_UntilX(startx, starty, text, &maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt, startx-1);
1028 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)
1030 return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]);
1033 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, float scalex, float scaley, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1035 return DrawQ_String_Font_UntilX(0, 0, text, maxlen, scalex, scaley, 1, 1, 1, 0, 0, NULL, ignorecolorcodes, fnt, maxWidth);
1038 float DrawQ_TextWidth_Font(const char *text, size_t maxlen, float scalex, float scaley, qboolean ignorecolorcodes, const dp_font_t *fnt)
1040 return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, scalex, scaley, ignorecolorcodes, fnt, -1);
1045 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1047 int color, numchars = 0;
1048 char *outputend2c = output2c + maxoutchars - 2;
1049 if (!outcolor || *outcolor == -1)
1050 color = STRING_COLOR_DEFAULT;
1054 maxreadchars = 1<<30;
1055 textend = text + maxreadchars;
1056 while (text != textend && *text)
1058 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1060 if (text[1] == STRING_COLOR_TAG)
1062 else if (text[1] >= '0' && text[1] <= '9')
1064 color = text[1] - '0';
1069 if (output2c >= outputend2c)
1071 *output2c++ = *text++;
1072 *output2c++ = color;
1075 output2c[0] = output2c[1] = 0;
1082 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)
1086 _DrawQ_ProcessDrawFlag(flags);
1088 R_Mesh_VertexPointer(floats, 0, 0);
1089 R_Mesh_ColorPointer(floats + 20, 0, 0);
1090 R_Mesh_ResetTextureState();
1096 height = pic->height;
1097 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1098 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1099 floats[12] = s1;floats[13] = t1;
1100 floats[14] = s2;floats[15] = t2;
1101 floats[16] = s4;floats[17] = t4;
1102 floats[18] = s3;floats[19] = t3;
1105 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1106 floats[0] = floats[9] = x;
1107 floats[1] = floats[4] = y;
1108 floats[3] = floats[6] = x + width;
1109 floats[7] = floats[10] = y + height;
1110 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1111 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1112 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1113 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1115 R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
1118 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1120 _DrawQ_ProcessDrawFlag(flags);
1122 R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1123 R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1124 R_Mesh_ResetTextureState();
1125 R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
1126 R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1128 GL_LockArrays(0, mesh->num_vertices);
1129 R_Mesh_Draw(0, mesh->num_vertices, mesh->num_triangles, mesh->data_element3i, 0, 0);
1130 GL_LockArrays(0, 0);
1133 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1137 _DrawQ_ProcessDrawFlag(flags);
1141 qglBegin(GL_LINE_LOOP);
1142 for (num = 0;num < mesh->num_vertices;num++)
1144 if (mesh->data_color4f)
1145 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]);
1146 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1152 //[515]: this is old, delete
1153 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1155 _DrawQ_ProcessDrawFlag(flags);
1158 qglLineWidth(width);CHECKGLERROR
1160 GL_Color(r,g,b,alpha);
1163 qglVertex2f(x1, y1);
1164 qglVertex2f(x2, y2);
1169 void DrawQ_SetClipArea(float x, float y, float width, float height)
1173 // We have to convert the con coords into real coords
1174 // OGL uses top to bottom
1175 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)));
1177 GL_ScissorTest(true);
1180 void DrawQ_ResetClipArea(void)
1183 GL_ScissorTest(false);
1186 void DrawQ_Finish(void)
1188 r_refdef.draw2dstage = false;
1191 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1192 void R_DrawGamma(void)
1195 if (!vid_usinghwgamma)
1197 // all the blends ignore depth
1198 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1199 R_Mesh_ColorPointer(NULL, 0, 0);
1200 R_Mesh_ResetTextureState();
1202 GL_DepthRange(0, 1);
1203 GL_PolygonOffset(0, 0);
1204 GL_DepthTest(false);
1205 if (v_color_enable.integer)
1207 c[0] = v_color_white_r.value;
1208 c[1] = v_color_white_g.value;
1209 c[2] = v_color_white_b.value;
1212 c[0] = c[1] = c[2] = v_contrast.value;
1213 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1215 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1216 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1218 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1219 R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);
1220 VectorScale(c, 0.5, c);
1223 if (v_color_enable.integer)
1225 c[0] = v_color_black_r.value;
1226 c[1] = v_color_black_g.value;
1227 c[2] = v_color_black_b.value;
1230 c[0] = c[1] = c[2] = v_brightness.value;
1231 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1233 GL_BlendFunc(GL_ONE, GL_ONE);
1234 GL_Color(c[0], c[1], c[2], 1);
1235 R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);