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 cachepic_t *r_crosshairs[NUMCROSSHAIRS+1];
35 //=============================================================================
36 /* Support Routines */
38 #define FONT_FILESIZE 13468
39 #define MAX_CACHED_PICS 1024
40 #define CACHEPICHASHSIZE 256
41 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
42 static cachepic_t cachepics[MAX_CACHED_PICS];
43 static int numcachepics;
45 static rtexturepool_t *drawtexturepool;
47 static unsigned char concharimage[FONT_FILESIZE] =
52 static rtexture_t *draw_generateconchars(void)
55 unsigned char buffer[65536][4], *data = NULL;
58 data = LoadTGA_BGRA (concharimage, FONT_FILESIZE);
60 for (i = 0;i < 8192;i++)
62 random = lhrandom (0.0,1.0);
63 buffer[i][2] = 83 + (unsigned char)(random * 64);
64 buffer[i][1] = 71 + (unsigned char)(random * 32);
65 buffer[i][0] = 23 + (unsigned char)(random * 16);
66 buffer[i][3] = data[i*4+0];
69 for (i = 8192;i < 32768;i++)
71 random = lhrandom (0.0,1.0);
72 buffer[i][2] = 95 + (unsigned char)(random * 64);
73 buffer[i][1] = 95 + (unsigned char)(random * 64);
74 buffer[i][0] = 95 + (unsigned char)(random * 64);
75 buffer[i][3] = data[i*4+0];
78 for (i = 32768;i < 40960;i++)
80 random = lhrandom (0.0,1.0);
81 buffer[i][2] = 83 + (unsigned char)(random * 64);
82 buffer[i][1] = 71 + (unsigned char)(random * 32);
83 buffer[i][0] = 23 + (unsigned char)(random * 16);
84 buffer[i][3] = data[i*4+0];
87 for (i = 40960;i < 65536;i++)
89 random = lhrandom (0.0,1.0);
90 buffer[i][2] = 96 + (unsigned char)(random * 64);
91 buffer[i][1] = 43 + (unsigned char)(random * 32);
92 buffer[i][0] = 27 + (unsigned char)(random * 32);
93 buffer[i][3] = data[i*4+0];
97 Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, &buffer[0][0]);
101 return R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, &buffer[0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
104 static char *pointerimage =
123 static rtexture_t *draw_generatemousepointer(void)
126 unsigned char buffer[256][4];
127 for (i = 0;i < 256;i++)
129 if (pointerimage[i] == '.')
138 buffer[i][0] = (pointerimage[i] - '0') * 16;
139 buffer[i][1] = (pointerimage[i] - '0') * 16;
140 buffer[i][2] = (pointerimage[i] - '0') * 16;
144 return R_LoadTexture2D(drawtexturepool, "mousepointer", 16, 16, &buffer[0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
147 static char *crosshairtexdata[NUMCROSSHAIRS] =
252 static rtexture_t *draw_generatecrosshair(int num)
256 unsigned char data[16*16][4];
257 in = crosshairtexdata[num];
258 for (i = 0;i < 16*16;i++)
269 data[i][0] = data[i][1] = data[i][2] = (unsigned char) ((int) (in[i] - '0') * 127 / 7 + 128);
273 return R_LoadTexture2D(drawtexturepool, va("crosshair%i", num+1), 16, 16, &data[0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
276 static rtexture_t *draw_generateditherpattern(void)
280 unsigned char data[8*8*4];
281 for (y = 0;y < 8;y++)
283 for (x = 0;x < 8;x++)
285 data[(y*8+x)*4+0] = data[(y*8+x)*4+1] = data[(y*8+x)*4+2] = ((x^y) & 4) ? 255 : 0;
286 data[(y*8+x)*4+3] = 255;
289 return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, data, TEXTYPE_BGRA, TEXF_FORCENEAREST | TEXF_PRECACHE, NULL);
291 unsigned char data[16];
292 memset(data, 255, sizeof(data));
293 data[0] = data[1] = data[2] = data[12] = data[13] = data[14] = 0;
294 return R_LoadTexture2D(drawtexturepool, "ditherpattern", 2, 2, data, TEXTYPE_BGRA, TEXF_FORCENEAREST | TEXF_PRECACHE, NULL);
303 // FIXME: move this to client somehow
304 static cachepic_t *Draw_CachePic_Compression (const char *path, qboolean persistent, qboolean allow_compression)
310 unsigned char *lmpdata;
311 char lmpname[MAX_QPATH];
313 // check whether the picture has already been cached
314 crc = CRC_Block((unsigned char *)path, strlen(path));
315 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
316 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
317 if (!strcmp (path, pic->name))
320 if (numcachepics == MAX_CACHED_PICS)
322 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
323 // FIXME: support NULL in callers?
324 return cachepics; // return the first one
326 pic = cachepics + (numcachepics++);
327 strlcpy (pic->name, path, sizeof(pic->name));
329 pic->chain = cachepichash[hashkey];
330 cachepichash[hashkey] = pic;
332 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
333 pic->tex = CL_GetDynTexture( path );
334 // if so, set the width/height, too
336 pic->width = R_TextureWidth(pic->tex);
337 pic->height = R_TextureHeight(pic->tex);
338 // we're done now (early-out)
344 flags |= TEXF_PRECACHE;
345 if (!strcmp(path, "gfx/colorcontrol/ditherpattern"))
347 if(allow_compression && gl_texturecompression_2d.integer)
348 flags |= TEXF_COMPRESS;
350 // load a high quality image from disk if possible
351 pic->tex = loadtextureimage(drawtexturepool, path, false, flags, true);
352 if (pic->tex == NULL && !strncmp(path, "gfx/", 4))
354 // compatibility with older versions which did not require gfx/ prefix
355 pic->tex = loadtextureimage(drawtexturepool, path + 4, false, flags, true);
357 // if a high quality image was loaded, set the pic's size to match it, just
358 // in case there's no low quality version to get the size from
361 pic->width = R_TextureWidth(pic->tex);
362 pic->height = R_TextureHeight(pic->tex);
365 // now read the low quality version (wad or lmp file), and take the pic
366 // size from that even if we don't upload the texture, this way the pics
367 // show up the right size in the menu even if they were replaced with
368 // higher or lower resolution versions
369 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
370 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
374 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
375 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
376 // if no high quality replacement image was found, upload the original low quality texture
378 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
382 else if ((lmpdata = W_GetLumpName (path + 4)))
384 if (!strcmp(path, "gfx/conchars"))
386 // conchars is a raw image and with color 0 as transparent instead of 255
389 // if no high quality replacement image was found, upload the original low quality texture
391 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_font);
395 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
396 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
397 // if no high quality replacement image was found, upload the original low quality texture
399 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
403 // if it's not found on disk, check if it's one of the builtin images
404 if (pic->tex == NULL)
406 if (pic->tex == NULL && !strcmp(path, "gfx/conchars"))
407 pic->tex = draw_generateconchars();
408 if (pic->tex == NULL && !strcmp(path, "ui/mousepointer"))
409 pic->tex = draw_generatemousepointer();
410 if (pic->tex == NULL && !strcmp(path, "gfx/prydoncursor001"))
411 pic->tex = draw_generatemousepointer();
412 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair1"))
413 pic->tex = draw_generatecrosshair(0);
414 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair2"))
415 pic->tex = draw_generatecrosshair(1);
416 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair3"))
417 pic->tex = draw_generatecrosshair(2);
418 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair4"))
419 pic->tex = draw_generatecrosshair(3);
420 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair5"))
421 pic->tex = draw_generatecrosshair(4);
422 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair6"))
423 pic->tex = draw_generatecrosshair(5);
424 if (pic->tex == NULL && !strcmp(path, "gfx/colorcontrol/ditherpattern"))
425 pic->tex = draw_generateditherpattern();
426 // default textures for light sprites
427 // todo: improve them
428 if (pic->tex == NULL && !strcmp(path, "gfx/editlights/cursor"))
429 pic->tex = draw_generatecrosshair(0);
430 if (pic->tex == NULL && !strcmp(path, "gfx/editlights/light"))
431 pic->tex = draw_generatecrosshair(0);
432 if (pic->tex == NULL && !strcmp(path, "gfx/editlights/noshadow"))
433 pic->tex = draw_generatecrosshair(0);
434 if (pic->tex == NULL && !strcmp(path, "gfx/editlights/cubemap"))
435 pic->tex = draw_generatecrosshair(0);
436 if (pic->tex == NULL && !strcmp(path, "gfx/editlights/selection"))
437 pic->tex = draw_generatecrosshair(0);
438 if (pic->tex == NULL)
440 // don't complain about missing gfx/crosshair images
441 if (strncmp(path, "gfx/crosshair", 13))
442 Con_Printf("Draw_CachePic: failed to load %s\n", path);
443 pic->tex = r_texture_notexture;
445 pic->width = R_TextureWidth(pic->tex);
446 pic->height = R_TextureHeight(pic->tex);
451 cachepic_t *Draw_CachePic (const char *path, qboolean persistent)
453 return Draw_CachePic_Compression(path, persistent, true);
456 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
461 crc = CRC_Block((unsigned char *)picname, strlen(picname));
462 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
463 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
464 if (!strcmp (picname, pic->name))
469 if (pic->tex && pic->width == width && pic->height == height)
471 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
479 if (numcachepics == MAX_CACHED_PICS)
481 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
482 // FIXME: support NULL in callers?
483 return cachepics; // return the first one
485 pic = cachepics + (numcachepics++);
486 strlcpy (pic->name, picname, sizeof(pic->name));
488 pic->chain = cachepichash[hashkey];
489 cachepichash[hashkey] = pic;
494 pic->height = height;
496 R_FreeTexture(pic->tex);
497 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, alpha ? TEXF_ALPHA : 0, NULL);
501 void Draw_FreePic(const char *picname)
506 // this doesn't really free the pic, but does free it's texture
507 crc = CRC_Block((unsigned char *)picname, strlen(picname));
508 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
509 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
511 if (!strcmp (picname, pic->name) && pic->tex)
513 R_FreeTexture(pic->tex);
521 extern int con_linewidth; // to force rewrapping
522 static void LoadFont(qboolean override, const char *name, dp_font_t *fnt)
526 char widthfile[MAX_QPATH];
528 fs_offset_t widthbufsize;
530 if(override || !fnt->texpath[0])
531 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
533 if(drawtexturepool == NULL)
534 return; // before gl_draw_start, so will be loaded later
536 fnt->tex = Draw_CachePic_Compression(fnt->texpath, true, false)->tex;
537 if(fnt->tex == r_texture_notexture)
539 fnt->tex = Draw_CachePic_Compression("gfx/conchars", true, false)->tex;
540 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
543 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
545 // unspecified width == 1 (base width)
546 for(i = 1; i < 256; ++i)
547 fnt->width_of[i] = 1;
549 // FIXME load "name.width", if it fails, fill all with 1
550 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
552 float extraspacing = 0;
553 const char *p = widthbuf;
558 if(!COM_ParseToken_Simple(&p, false, false))
561 if(!strcmp(com_token, "extraspacing"))
563 if(!COM_ParseToken_Simple(&p, false, false))
565 extraspacing = atof(com_token);
568 fnt->width_of[ch++] = atof(com_token) + extraspacing;
574 maxwidth = fnt->width_of[1];
575 for(i = 2; i < 256; ++i)
576 maxwidth = max(maxwidth, fnt->width_of[i]);
577 fnt->width_of[0] = maxwidth;
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 for (i = 1;i <= NUMCROSSHAIRS;i++)
629 r_crosshairs[i] = Draw_CachePic(va("gfx/crosshair%i", i), true);
631 // draw the loading screen so people have something to see in the newly opened window
632 SCR_UpdateLoadingScreen(true);
635 static void gl_draw_shutdown(void)
637 R_FreeTexturePool(&drawtexturepool);
640 memset(cachepichash, 0, sizeof(cachepichash));
643 static void gl_draw_newmap(void)
647 void GL_Draw_Init (void)
650 Cvar_RegisterVariable(&r_textshadow);
651 Cvar_RegisterVariable(&r_textbrightness);
652 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
653 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
655 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
656 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
657 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
658 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
659 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
660 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
661 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
662 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
663 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
664 for(i = 0, j = 0; i < MAX_FONTS; ++i)
665 if(!FONT_USER[i].title[0])
666 dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++);
669 static void _DrawQ_Setup(void)
671 if (r_refdef.draw2dstage)
673 r_refdef.draw2dstage = true;
675 qglViewport(r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
676 GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
677 GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
678 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
679 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
680 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
681 R_Mesh_Matrix(&identitymatrix);
685 GL_PolygonOffset(0, 0);
689 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
691 if (gl_support_fragment_shader)
693 qglUseProgramObjectARB(0);CHECKGLERROR
697 static void _DrawQ_ProcessDrawFlag(int flags)
701 if(flags == DRAWFLAG_ADDITIVE)
702 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
703 else if(flags == DRAWFLAG_MODULATE)
704 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
705 else if(flags == DRAWFLAG_2XMODULATE)
706 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
708 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
711 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
715 _DrawQ_ProcessDrawFlag(flags);
716 GL_Color(red, green, blue, alpha);
718 R_Mesh_VertexPointer(floats, 0, 0);
719 R_Mesh_ColorPointer(NULL, 0, 0);
720 R_Mesh_ResetTextureState();
726 height = pic->height;
727 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
728 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
730 // AK07: lets be texel correct on the corners
732 float horz_offset = 0.5f / pic->width;
733 float vert_offset = 0.5f / pic->height;
735 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
736 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
737 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
738 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
742 floats[2] = floats[5] = floats[8] = floats[11] = 0;
743 floats[0] = floats[9] = x;
744 floats[1] = floats[4] = y;
745 floats[3] = floats[6] = x + width;
746 floats[7] = floats[10] = y + height;
748 R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
751 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
755 _DrawQ_ProcessDrawFlag(flags);
756 GL_Color(red, green, blue, alpha);
758 R_Mesh_VertexPointer(floats, 0, 0);
759 R_Mesh_ColorPointer(NULL, 0, 0);
760 R_Mesh_ResetTextureState();
762 floats[2] = floats[5] = floats[8] = floats[11] = 0;
763 floats[0] = floats[9] = x;
764 floats[1] = floats[4] = y;
765 floats[3] = floats[6] = x + width;
766 floats[7] = floats[10] = y + height;
768 R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
771 // color tag printing
772 static vec4_t string_colors[] =
775 // LordHavoc: why on earth is cyan before magenta in Quake3?
776 // LordHavoc: note: Doom3 uses white for [0] and [7]
777 {0.0, 0.0, 0.0, 1.0}, // black
778 {1.0, 0.0, 0.0, 1.0}, // red
779 {0.0, 1.0, 0.0, 1.0}, // green
780 {1.0, 1.0, 0.0, 1.0}, // yellow
781 {0.0, 0.0, 1.0, 1.0}, // blue
782 {0.0, 1.0, 1.0, 1.0}, // cyan
783 {1.0, 0.0, 1.0, 1.0}, // magenta
784 {1.0, 1.0, 1.0, 1.0}, // white
785 // [515]'s BX_COLOREDTEXT extension
786 {1.0, 1.0, 1.0, 0.5}, // half transparent
787 {0.5, 0.5, 0.5, 1.0} // half brightness
788 // Black's color table
789 //{1.0, 1.0, 1.0, 1.0},
790 //{1.0, 0.0, 0.0, 1.0},
791 //{0.0, 1.0, 0.0, 1.0},
792 //{0.0, 0.0, 1.0, 1.0},
793 //{1.0, 1.0, 0.0, 1.0},
794 //{0.0, 1.0, 1.0, 1.0},
795 //{1.0, 0.0, 1.0, 1.0},
796 //{0.1, 0.1, 0.1, 1.0}
799 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
801 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
803 float v = r_textbrightness.value;
804 Vector4Copy(string_colors[colorindex], color);
805 Vector4Set(color, (color[0] * (1-v) + v) * r, (color[1] * (1-v) + v) * g, (color[2] * (1-v) + v) * b, color[3] * a);
808 float shadowalpha = color[0]+color[1]+color[2] * 0.8;
809 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
813 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)
815 int num, shadow, colorindex = STRING_COLOR_DEFAULT;
817 float x = startx, y, s, t, u, v;
821 float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
822 float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
823 float color4f[QUADELEMENTS_MAXQUADS*4*4];
829 // when basealpha == 0, skip as much as possible (just return width)
832 _DrawQ_ProcessDrawFlag(flags);
834 R_Mesh_ColorPointer(color4f, 0, 0);
835 R_Mesh_ResetTextureState();
836 R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
837 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
838 R_Mesh_VertexPointer(vertex3f, 0, 0);
845 checkwidth = (maxx >= startx);
847 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
849 if (!outcolor || *outcolor == -1)
850 colorindex = STRING_COLOR_DEFAULT;
852 colorindex = *outcolor;
854 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
860 x += r_textshadow.value;
861 y += r_textshadow.value;
863 for (i = 0;i < *maxlen && text[i];i++)
868 if(x + fnt->width_of[' '] * w > maxx)
869 break; // oops, can't draw this
870 x += fnt->width_of[' '] * w;
873 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen)
875 if (text[i+1] == STRING_COLOR_TAG)
879 else if (text[i+1] >= '0' && text[i+1] <= '9')
881 colorindex = text[i+1] - '0';
882 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
887 num = (unsigned char) text[i];
889 if(x + fnt->width_of[num] * w > maxx)
890 break; // oops, can't draw this
893 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
894 s = (num & 15)*0.0625f + (0.5f / 256.0f);
895 t = (num >> 4)*0.0625f + (0.5f / 256.0f);
896 u = 0.0625f - (1.0f / 256.0f);
897 v = 0.0625f - (1.0f / 256.0f);
898 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
899 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
900 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
901 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
902 at[ 0] = s ;at[ 1] = t ;
903 at[ 2] = s+u;at[ 3] = t ;
904 at[ 4] = s+u;at[ 5] = t+v;
905 at[ 6] = s ;at[ 7] = t+v;
906 av[ 0] = x ;av[ 1] = y ;av[ 2] = 10;
907 av[ 3] = x+w;av[ 4] = y ;av[ 5] = 10;
908 av[ 6] = x+w;av[ 7] = y+h;av[ 8] = 10;
909 av[ 9] = x ;av[10] = y+h;av[11] = 10;
914 if (batchcount >= QUADELEMENTS_MAXQUADS)
916 if (basealpha >= (1.0f / 255.0f))
918 GL_LockArrays(0, batchcount * 4);
919 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
928 x += fnt->width_of[num] * w;
932 checkwidth = 0; // we've done all we had to
938 GL_LockArrays(0, batchcount * 4);
939 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
945 *outcolor = colorindex;
947 // note: this relies on the proper text (not shadow) being drawn last
951 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)
953 return DrawQ_String_Font_UntilX(startx, starty, text, &maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt, startx-1);
956 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)
958 return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]);
961 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, float scalex, float scaley, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
963 return DrawQ_String_Font_UntilX(0, 0, text, maxlen, scalex, scaley, 1, 1, 1, 0, 0, NULL, ignorecolorcodes, fnt, maxWidth);
966 float DrawQ_TextWidth_Font(const char *text, size_t maxlen, float scalex, float scaley, qboolean ignorecolorcodes, const dp_font_t *fnt)
968 return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, scalex, scaley, ignorecolorcodes, fnt, -1);
973 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
975 int color, numchars = 0;
976 char *outputend2c = output2c + maxoutchars - 2;
977 if (!outcolor || *outcolor == -1)
978 color = STRING_COLOR_DEFAULT;
982 maxreadchars = 1<<30;
983 textend = text + maxreadchars;
984 while (text != textend && *text)
986 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
988 if (text[1] == STRING_COLOR_TAG)
990 else if (text[1] >= '0' && text[1] <= '9')
992 color = text[1] - '0';
997 if (output2c >= outputend2c)
999 *output2c++ = *text++;
1000 *output2c++ = color;
1003 output2c[0] = output2c[1] = 0;
1010 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)
1014 _DrawQ_ProcessDrawFlag(flags);
1016 R_Mesh_VertexPointer(floats, 0, 0);
1017 R_Mesh_ColorPointer(floats + 20, 0, 0);
1018 R_Mesh_ResetTextureState();
1024 height = pic->height;
1025 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1026 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1027 floats[12] = s1;floats[13] = t1;
1028 floats[14] = s2;floats[15] = t2;
1029 floats[16] = s4;floats[17] = t4;
1030 floats[18] = s3;floats[19] = t3;
1033 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1034 floats[0] = floats[9] = x;
1035 floats[1] = floats[4] = y;
1036 floats[3] = floats[6] = x + width;
1037 floats[7] = floats[10] = y + height;
1038 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1039 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1040 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1041 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1043 R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
1046 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1048 _DrawQ_ProcessDrawFlag(flags);
1050 R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1051 R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1052 R_Mesh_ResetTextureState();
1053 R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
1054 R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1056 GL_LockArrays(0, mesh->num_vertices);
1057 R_Mesh_Draw(0, mesh->num_vertices, mesh->num_triangles, mesh->data_element3i, 0, 0);
1058 GL_LockArrays(0, 0);
1061 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1065 _DrawQ_ProcessDrawFlag(flags);
1069 qglBegin(GL_LINE_LOOP);
1070 for (num = 0;num < mesh->num_vertices;num++)
1072 if (mesh->data_color4f)
1073 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]);
1074 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1080 //[515]: this is old, delete
1081 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1083 _DrawQ_ProcessDrawFlag(flags);
1086 qglLineWidth(width);CHECKGLERROR
1088 GL_Color(r,g,b,alpha);
1091 qglVertex2f(x1, y1);
1092 qglVertex2f(x2, y2);
1097 void DrawQ_SetClipArea(float x, float y, float width, float height)
1101 // We have to convert the con coords into real coords
1102 // OGL uses top to bottom
1103 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)));
1105 GL_ScissorTest(true);
1108 void DrawQ_ResetClipArea(void)
1111 GL_ScissorTest(false);
1114 void DrawQ_Finish(void)
1116 r_refdef.draw2dstage = false;
1119 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1120 void R_DrawGamma(void)
1123 if (!vid_usinghwgamma)
1125 // all the blends ignore depth
1126 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1127 R_Mesh_ColorPointer(NULL, 0, 0);
1128 R_Mesh_ResetTextureState();
1130 GL_DepthRange(0, 1);
1131 GL_PolygonOffset(0, 0);
1132 GL_DepthTest(false);
1133 if (v_color_enable.integer)
1135 c[0] = v_color_white_r.value;
1136 c[1] = v_color_white_g.value;
1137 c[2] = v_color_white_b.value;
1140 c[0] = c[1] = c[2] = v_contrast.value;
1141 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1143 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1144 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1146 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1147 R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);
1148 VectorScale(c, 0.5, c);
1151 if (v_color_enable.integer)
1153 c[0] = v_color_black_r.value;
1154 c[1] = v_color_black_g.value;
1155 c[2] = v_color_black_b.value;
1158 c[0] = c[1] = c[2] = v_brightness.value;
1159 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1161 GL_BlendFunc(GL_ONE, GL_ONE);
1162 GL_Color(c[0], c[1], c[2], 1);
1163 R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);