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,
278 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
280 const embeddedpic_t *p;
281 for (p = embeddedpics;p->name;p++)
282 if (!strcmp(name, p->name))
283 return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA | TEXF_PRECACHE, palette_bgra_embeddedpic);
284 if (!strcmp(name, "gfx/conchars"))
285 return draw_generateconchars();
286 if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
287 return draw_generateditherpattern();
289 Con_Printf("Draw_CachePic: failed to load %s\n", name);
290 return r_texture_notexture;
299 // FIXME: move this to client somehow
300 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
306 unsigned char *lmpdata;
307 char lmpname[MAX_QPATH];
309 // check whether the picture has already been cached
310 crc = CRC_Block((unsigned char *)path, strlen(path));
311 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
312 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
313 if (!strcmp (path, pic->name))
316 if (numcachepics == MAX_CACHED_PICS)
318 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
319 // FIXME: support NULL in callers?
320 return cachepics; // return the first one
322 pic = cachepics + (numcachepics++);
323 strlcpy (pic->name, path, sizeof(pic->name));
325 pic->chain = cachepichash[hashkey];
326 cachepichash[hashkey] = pic;
328 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
329 pic->tex = CL_GetDynTexture( path );
330 // if so, set the width/height, too
332 pic->width = R_TextureWidth(pic->tex);
333 pic->height = R_TextureHeight(pic->tex);
334 // we're done now (early-out)
339 if (!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
340 flags |= TEXF_PRECACHE;
341 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
343 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer)
344 flags |= TEXF_COMPRESS;
346 // load a high quality image from disk if possible
347 pic->tex = loadtextureimage(drawtexturepool, path, false, flags, true);
348 if (pic->tex == NULL && !strncmp(path, "gfx/", 4))
350 // compatibility with older versions which did not require gfx/ prefix
351 pic->tex = loadtextureimage(drawtexturepool, path + 4, false, flags, true);
353 // if a high quality image was loaded, set the pic's size to match it, just
354 // in case there's no low quality version to get the size from
357 pic->width = R_TextureWidth(pic->tex);
358 pic->height = R_TextureHeight(pic->tex);
361 // now read the low quality version (wad or lmp file), and take the pic
362 // size from that even if we don't upload the texture, this way the pics
363 // show up the right size in the menu even if they were replaced with
364 // higher or lower resolution versions
365 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
366 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
368 if (developer_loading.integer)
369 Con_Printf("loading lump \"%s\"\n", path);
373 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
374 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
375 // if no high quality replacement image was found, upload the original low quality texture
377 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
381 else if ((lmpdata = W_GetLumpName (path + 4)))
383 if (developer_loading.integer)
384 Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
386 if (!strcmp(path, "gfx/conchars"))
388 // conchars is a raw image and with color 0 as transparent instead of 255
391 // if no high quality replacement image was found, upload the original low quality texture
393 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_font);
397 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
398 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
399 // if no high quality replacement image was found, upload the original low quality texture
401 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
405 // if it's not found on disk, generate an image
406 if (pic->tex == NULL)
408 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
409 pic->width = R_TextureWidth(pic->tex);
410 pic->height = R_TextureHeight(pic->tex);
416 cachepic_t *Draw_CachePic (const char *path)
418 return Draw_CachePic_Flags (path, 0);
421 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
426 crc = CRC_Block((unsigned char *)picname, strlen(picname));
427 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
428 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
429 if (!strcmp (picname, pic->name))
434 if (pic->tex && pic->width == width && pic->height == height)
436 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
444 if (numcachepics == MAX_CACHED_PICS)
446 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
447 // FIXME: support NULL in callers?
448 return cachepics; // return the first one
450 pic = cachepics + (numcachepics++);
451 strlcpy (pic->name, picname, sizeof(pic->name));
453 pic->chain = cachepichash[hashkey];
454 cachepichash[hashkey] = pic;
459 pic->height = height;
461 R_FreeTexture(pic->tex);
462 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, alpha ? TEXF_ALPHA : 0, NULL);
466 void Draw_FreePic(const char *picname)
471 // this doesn't really free the pic, but does free it's texture
472 crc = CRC_Block((unsigned char *)picname, strlen(picname));
473 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
474 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
476 if (!strcmp (picname, pic->name) && pic->tex)
478 R_FreeTexture(pic->tex);
486 extern int con_linewidth; // to force rewrapping
487 static void LoadFont(qboolean override, const char *name, dp_font_t *fnt)
490 float maxwidth, scale;
491 char widthfile[MAX_QPATH];
493 fs_offset_t widthbufsize;
495 if(override || !fnt->texpath[0])
496 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
498 if(drawtexturepool == NULL)
499 return; // before gl_draw_start, so will be loaded later
501 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
502 if(fnt->tex == r_texture_notexture)
504 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
505 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
508 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
510 // unspecified width == 1 (base width)
511 for(i = 1; i < 256; ++i)
512 fnt->width_of[i] = 1;
515 // FIXME load "name.width", if it fails, fill all with 1
516 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
518 float extraspacing = 0;
519 const char *p = widthbuf;
524 if(!COM_ParseToken_Simple(&p, false, false))
542 fnt->width_of[ch++] = atof(com_token) + extraspacing;
545 if(!strcmp(com_token, "extraspacing"))
547 if(!COM_ParseToken_Simple(&p, false, false))
549 extraspacing = atof(com_token);
551 else if(!strcmp(com_token, "scale"))
553 if(!COM_ParseToken_Simple(&p, false, false))
555 scale = atof(com_token);
559 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
560 if(!COM_ParseToken_Simple(&p, false, false))
570 maxwidth = fnt->width_of[1];
571 for(i = 2; i < 256; ++i)
572 maxwidth = max(maxwidth, fnt->width_of[i]);
573 fnt->maxwidth = maxwidth;
575 // fix up maxwidth for overlap
576 fnt->maxwidth *= scale;
579 if(fnt == FONT_CONSOLE)
580 con_linewidth = -1; // rewrap console in next frame
583 static dp_font_t *FindFont(const char *title)
586 for(i = 0; i < MAX_FONTS; ++i)
587 if(!strcmp(dp_fonts[i].title, title))
592 static void LoadFont_f(void)
598 Con_Printf("Available font commands:\n");
599 for(i = 0; i < MAX_FONTS; ++i)
600 Con_Printf(" loadfont %s gfx/tgafile\n", dp_fonts[i].title);
603 f = FindFont(Cmd_Argv(1));
606 Con_Printf("font function not found\n");
609 LoadFont(true, (Cmd_Argc() < 3) ? "gfx/conchars" : Cmd_Argv(2), f);
617 static void gl_draw_start(void)
620 drawtexturepool = R_AllocTexturePool();
623 memset(cachepichash, 0, sizeof(cachepichash));
625 for(i = 0; i < MAX_FONTS; ++i)
626 LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i]);
628 // draw the loading screen so people have something to see in the newly opened window
629 SCR_UpdateLoadingScreen(true);
632 static void gl_draw_shutdown(void)
634 R_FreeTexturePool(&drawtexturepool);
637 memset(cachepichash, 0, sizeof(cachepichash));
640 static void gl_draw_newmap(void)
644 void GL_Draw_Init (void)
647 Cvar_RegisterVariable(&r_textshadow);
648 Cvar_RegisterVariable(&r_textbrightness);
649 Cvar_RegisterVariable(&r_textcontrast);
650 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
651 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
653 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
654 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
655 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
656 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
657 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
658 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
659 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
660 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
661 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
662 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
663 if(!FONT_USER[i].title[0])
664 dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++);
667 void _DrawQ_Setup(void)
669 r_viewport_t viewport;
670 if (r_refdef.draw2dstage)
672 r_refdef.draw2dstage = true;
674 R_Viewport_InitOrtho(&viewport, &identitymatrix, r_refdef.view.x, vid.height - r_refdef.view.y - r_refdef.view.height, r_refdef.view.width, r_refdef.view.height, 0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100, NULL);
675 R_SetViewport(&viewport);
676 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
677 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
678 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
679 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
680 R_Mesh_Matrix(&identitymatrix);
684 GL_PolygonOffset(0, 0);
688 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
690 R_SetupGenericShader(true);
693 static void _DrawQ_ProcessDrawFlag(int flags)
697 if(flags == DRAWFLAG_ADDITIVE)
698 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
699 else if(flags == DRAWFLAG_MODULATE)
700 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
701 else if(flags == DRAWFLAG_2XMODULATE)
702 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
703 else if(flags == DRAWFLAG_SCREEN)
704 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
706 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
709 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
713 _DrawQ_ProcessDrawFlag(flags);
714 GL_Color(red, green, blue, alpha);
716 R_Mesh_VertexPointer(floats, 0, 0);
717 R_Mesh_ColorPointer(NULL, 0, 0);
718 R_Mesh_ResetTextureState();
719 R_SetupGenericShader(pic != NULL);
725 height = pic->height;
726 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
727 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
730 floats[12] = 0.0f;floats[13] = 0.0f;
731 floats[14] = 1.0f;floats[15] = 0.0f;
732 floats[16] = 1.0f;floats[17] = 1.0f;
733 floats[18] = 0.0f;floats[19] = 1.0f;
735 // AK07: lets be texel correct on the corners
737 float horz_offset = 0.5f / pic->width;
738 float vert_offset = 0.5f / pic->height;
740 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
741 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
742 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
743 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
748 floats[2] = floats[5] = floats[8] = floats[11] = 0;
749 floats[0] = floats[9] = x;
750 floats[1] = floats[4] = y;
751 floats[3] = floats[6] = x + width;
752 floats[7] = floats[10] = y + height;
754 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
757 void DrawQ_RotPic(float x, float y, cachepic_t *pic, float width, float height, float org_x, float org_y, float angle, float red, float green, float blue, float alpha, int flags)
760 float af = DEG2RAD(-angle); // forward
761 float ar = DEG2RAD(-angle + 90); // right
762 float sinaf = sin(af);
763 float cosaf = cos(af);
764 float sinar = sin(ar);
765 float cosar = cos(ar);
767 _DrawQ_ProcessDrawFlag(flags);
768 GL_Color(red, green, blue, alpha);
770 R_Mesh_VertexPointer(floats, 0, 0);
771 R_Mesh_ColorPointer(NULL, 0, 0);
772 R_Mesh_ResetTextureState();
773 R_SetupGenericShader(pic != NULL);
779 height = pic->height;
780 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
781 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
783 floats[12] = 0.0f;floats[13] = 0.0f;
784 floats[14] = 1.0f;floats[15] = 0.0f;
785 floats[16] = 1.0f;floats[17] = 1.0f;
786 floats[18] = 0.0f;floats[19] = 1.0f;
789 floats[2] = floats[5] = floats[8] = floats[11] = 0;
792 floats[0] = x - cosaf*org_x - cosar*org_y;
793 floats[1] = y - sinaf*org_x - sinar*org_y;
796 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
797 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
800 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
801 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
804 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
805 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
807 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
810 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
814 _DrawQ_ProcessDrawFlag(flags);
815 GL_Color(red, green, blue, alpha);
817 R_Mesh_VertexPointer(floats, 0, 0);
818 R_Mesh_ColorPointer(NULL, 0, 0);
819 R_Mesh_ResetTextureState();
820 R_SetupGenericShader(false);
822 floats[2] = floats[5] = floats[8] = floats[11] = 0;
823 floats[0] = floats[9] = x;
824 floats[1] = floats[4] = y;
825 floats[3] = floats[6] = x + width;
826 floats[7] = floats[10] = y + height;
828 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
831 /// color tag printing
832 static const vec4_t string_colors[] =
835 // LordHavoc: why on earth is cyan before magenta in Quake3?
836 // LordHavoc: note: Doom3 uses white for [0] and [7]
837 {0.0, 0.0, 0.0, 1.0}, // black
838 {1.0, 0.0, 0.0, 1.0}, // red
839 {0.0, 1.0, 0.0, 1.0}, // green
840 {1.0, 1.0, 0.0, 1.0}, // yellow
841 {0.0, 0.0, 1.0, 1.0}, // blue
842 {0.0, 1.0, 1.0, 1.0}, // cyan
843 {1.0, 0.0, 1.0, 1.0}, // magenta
844 {1.0, 1.0, 1.0, 1.0}, // white
845 // [515]'s BX_COLOREDTEXT extension
846 {1.0, 1.0, 1.0, 0.5}, // half transparent
847 {0.5, 0.5, 0.5, 1.0} // half brightness
848 // Black's color table
849 //{1.0, 1.0, 1.0, 1.0},
850 //{1.0, 0.0, 0.0, 1.0},
851 //{0.0, 1.0, 0.0, 1.0},
852 //{0.0, 0.0, 1.0, 1.0},
853 //{1.0, 1.0, 0.0, 1.0},
854 //{0.0, 1.0, 1.0, 1.0},
855 //{1.0, 0.0, 1.0, 1.0},
856 //{0.1, 0.1, 0.1, 1.0}
859 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
861 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
863 float C = r_textcontrast.value;
864 float B = r_textbrightness.value;
865 if (colorindex & 0x10000) // that bit means RGB color
867 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
868 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
869 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
870 color[3] = (colorindex & 0xf) / 15.0;
873 Vector4Copy(string_colors[colorindex], color);
874 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
877 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
878 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
882 float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
884 int num, colorindex = STRING_COLOR_DEFAULT;
893 if (!outcolor || *outcolor == -1)
894 colorindex = STRING_COLOR_DEFAULT;
896 colorindex = *outcolor;
898 maxwidth /= fnt->scale;
900 for (i = 0;i < *maxlen && text[i];i++)
904 if(x + fnt->width_of[(int) ' '] > maxwidth)
905 break; // oops, can't draw this
906 x += fnt->width_of[(int) ' '];
909 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen)
912 if (ch <= '9' && ch >= '0') // ^[0-9] found
914 colorindex = ch - '0';
917 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
919 // building colorindex...
920 ch = tolower(text[i+1]);
921 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
922 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
923 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
924 else tempcolorindex = 0;
927 ch = tolower(text[i+2]);
928 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
929 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
930 else tempcolorindex = 0;
933 ch = tolower(text[i+3]);
934 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
935 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
936 else tempcolorindex = 0;
939 colorindex = tempcolorindex | 0xf;
940 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
947 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
951 num = (unsigned char) text[i];
952 if(x + fnt->width_of[num] > maxwidth)
953 break; // oops, can't draw this
954 x += fnt->width_of[num];
960 *outcolor = colorindex;
962 return x * fnt->scale;
965 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)
967 int num, shadow, colorindex = STRING_COLOR_DEFAULT;
969 float x = startx, y, s, t, u, v, thisw;
973 float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
974 float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
975 float color4f[QUADELEMENTS_MAXQUADS*4*4];
980 tw = R_TextureWidth(fnt->tex);
981 th = R_TextureHeight(fnt->tex);
983 starty -= (fnt->scale - 1) * h * 0.5; // center
990 _DrawQ_ProcessDrawFlag(flags);
992 R_Mesh_ColorPointer(color4f, 0, 0);
993 R_Mesh_ResetTextureState();
994 R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
995 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
996 R_Mesh_VertexPointer(vertex3f, 0, 0);
997 R_SetupGenericShader(true);
1004 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1006 if (!outcolor || *outcolor == -1)
1007 colorindex = STRING_COLOR_DEFAULT;
1009 colorindex = *outcolor;
1011 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1017 x += r_textshadow.value;
1018 y += r_textshadow.value;
1020 for (i = 0;i < maxlen && text[i];i++)
1024 x += fnt->width_of[(int) ' '] * w;
1027 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen)
1030 if (ch <= '9' && ch >= '0') // ^[0-9] found
1032 colorindex = ch - '0';
1033 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1036 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1038 // building colorindex...
1039 ch = tolower(text[i+1]);
1040 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1041 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1042 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1043 else tempcolorindex = 0;
1046 ch = tolower(text[i+2]);
1047 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1048 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1049 else tempcolorindex = 0;
1052 ch = tolower(text[i+3]);
1053 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1054 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1055 else tempcolorindex = 0;
1058 colorindex = tempcolorindex | 0xf;
1059 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1060 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1061 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1068 else if (ch == STRING_COLOR_TAG)
1072 num = (unsigned char) text[i];
1073 thisw = fnt->width_of[num];
1074 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1075 s = (num & 15)*0.0625f + (0.5f / tw);
1076 t = (num >> 4)*0.0625f + (0.5f / th);
1077 u = 0.0625f * thisw - (1.0f / tw);
1078 v = 0.0625f - (1.0f / th);
1079 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1080 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1081 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1082 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1083 at[ 0] = s ; at[ 1] = t ;
1084 at[ 2] = s+u ; at[ 3] = t ;
1085 at[ 4] = s+u ; at[ 5] = t+v ;
1086 at[ 6] = s ; at[ 7] = t+v ;
1087 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1088 av[ 3] = x+w*thisw ; av[ 4] = y ; av[ 5] = 10;
1089 av[ 6] = x+w*thisw ; av[ 7] = y+h ; av[ 8] = 10;
1090 av[ 9] = x ; av[10] = y+h ; av[11] = 10;
1095 if (batchcount >= QUADELEMENTS_MAXQUADS)
1097 GL_LockArrays(0, batchcount * 4);
1098 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1099 GL_LockArrays(0, 0);
1110 GL_LockArrays(0, batchcount * 4);
1111 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1112 GL_LockArrays(0, 0);
1116 *outcolor = colorindex;
1118 // note: this relies on the proper text (not shadow) being drawn last
1122 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)
1124 return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]);
1127 float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt)
1129 return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, ignorecolorcodes, fnt, 1000000000);
1132 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1134 return DrawQ_TextWidth_Font_UntilWidth_TrackColors(text, maxlen, NULL, ignorecolorcodes, fnt, maxWidth);
1139 // no ^xrgb management
1140 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1142 int color, numchars = 0;
1143 char *outputend2c = output2c + maxoutchars - 2;
1144 if (!outcolor || *outcolor == -1)
1145 color = STRING_COLOR_DEFAULT;
1149 maxreadchars = 1<<30;
1150 textend = text + maxreadchars;
1151 while (text != textend && *text)
1153 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1155 if (text[1] == STRING_COLOR_TAG)
1157 else if (text[1] >= '0' && text[1] <= '9')
1159 color = text[1] - '0';
1164 if (output2c >= outputend2c)
1166 *output2c++ = *text++;
1167 *output2c++ = color;
1170 output2c[0] = output2c[1] = 0;
1177 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)
1181 _DrawQ_ProcessDrawFlag(flags);
1183 R_Mesh_VertexPointer(floats, 0, 0);
1184 R_Mesh_ColorPointer(floats + 20, 0, 0);
1185 R_Mesh_ResetTextureState();
1186 R_SetupGenericShader(pic != NULL);
1192 height = pic->height;
1193 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1194 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1195 floats[12] = s1;floats[13] = t1;
1196 floats[14] = s2;floats[15] = t2;
1197 floats[16] = s4;floats[17] = t4;
1198 floats[18] = s3;floats[19] = t3;
1201 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1202 floats[0] = floats[9] = x;
1203 floats[1] = floats[4] = y;
1204 floats[3] = floats[6] = x + width;
1205 floats[7] = floats[10] = y + height;
1206 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1207 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1208 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1209 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1211 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1214 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1216 _DrawQ_ProcessDrawFlag(flags);
1218 R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1219 R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1220 R_Mesh_ResetTextureState();
1221 R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
1222 R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1223 R_SetupGenericShader(mesh->texture != NULL);
1225 GL_LockArrays(0, mesh->num_vertices);
1226 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, mesh->data_element3s, 0, 0);
1227 GL_LockArrays(0, 0);
1230 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1234 _DrawQ_ProcessDrawFlag(flags);
1238 qglBegin(GL_LINE_LOOP);
1239 for (num = 0;num < mesh->num_vertices;num++)
1241 if (mesh->data_color4f)
1242 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]);
1243 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1249 //[515]: this is old, delete
1250 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1252 _DrawQ_ProcessDrawFlag(flags);
1254 R_SetupGenericShader(false);
1257 //qglLineWidth(width);CHECKGLERROR
1259 GL_Color(r,g,b,alpha);
1262 qglVertex2f(x1, y1);
1263 qglVertex2f(x2, y2);
1268 void DrawQ_SetClipArea(float x, float y, float width, float height)
1273 // We have to convert the con coords into real coords
1274 // OGL uses top to bottom
1275 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1276 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1277 iw = (int)(width * ((float)vid.width / vid_conwidth.integer));
1278 ih = (int)(height * ((float)vid.height / vid_conheight.integer));
1279 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1281 GL_ScissorTest(true);
1284 void DrawQ_ResetClipArea(void)
1287 GL_ScissorTest(false);
1290 void DrawQ_Finish(void)
1292 r_refdef.draw2dstage = false;
1295 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1296 void R_DrawGamma(void)
1299 if (!vid_usinghwgamma && !(r_glsl.integer && v_glslgamma.integer))
1301 // all the blends ignore depth
1302 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1303 R_Mesh_ColorPointer(NULL, 0, 0);
1304 R_Mesh_ResetTextureState();
1305 R_SetupGenericShader(false);
1307 GL_DepthRange(0, 1);
1308 GL_PolygonOffset(0, 0);
1309 GL_DepthTest(false);
1310 if (v_color_enable.integer)
1312 c[0] = v_color_white_r.value;
1313 c[1] = v_color_white_g.value;
1314 c[2] = v_color_white_b.value;
1317 c[0] = c[1] = c[2] = v_contrast.value;
1318 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1320 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1321 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1323 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1324 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
1325 VectorScale(c, 0.5, c);
1328 if (v_color_enable.integer)
1330 c[0] = v_color_black_r.value;
1331 c[1] = v_color_black_g.value;
1332 c[2] = v_color_black_b.value;
1335 c[0] = c[1] = c[2] = v_brightness.value;
1336 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1338 GL_BlendFunc(GL_ONE, GL_ONE);
1339 GL_Color(c[0], c[1], c[2], 1);
1340 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);