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"
29 #include "ft2_fontdefs.h"
32 static mempool_t *fonts_mempool = NULL;
34 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)"};
35 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)"};
36 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)"};
38 cvar_t r_font_postprocess_blur = {CVAR_SAVE, "r_font_postprocess_blur", "0", "font blur amount"};
39 cvar_t r_font_postprocess_outline = {CVAR_SAVE, "r_font_postprocess_outline", "0", "font outline amount"};
40 cvar_t r_font_postprocess_shadow_x = {CVAR_SAVE, "r_font_postprocess_shadow_x", "0", "font shadow X shift amount, applied during outlining"};
41 cvar_t r_font_postprocess_shadow_y = {CVAR_SAVE, "r_font_postprocess_shadow_y", "0", "font shadow Y shift amount, applied during outlining"};
42 cvar_t r_font_postprocess_shadow_z = {CVAR_SAVE, "r_font_postprocess_shadow_z", "0", "font shadow Z shift amount, applied during blurring"};
43 cvar_t r_font_hinting = {CVAR_SAVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"};
44 cvar_t r_font_antialias = {CVAR_SAVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */};
46 extern cvar_t v_glslgamma;
48 //=============================================================================
49 /* Support Routines */
51 #define FONT_FILESIZE 13468
52 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
53 static cachepic_t cachepics[MAX_CACHED_PICS];
54 static int numcachepics;
56 rtexturepool_t *drawtexturepool;
58 static const unsigned char concharimage[FONT_FILESIZE] =
63 static rtexture_t *draw_generateconchars(void)
70 data = LoadTGA_BGRA (concharimage, FONT_FILESIZE, NULL);
72 for (i = 0;i < 8192;i++)
74 random = lhrandom (0.0,1.0);
75 data[i*4+3] = data[i*4+0];
76 data[i*4+2] = 83 + (unsigned char)(random * 64);
77 data[i*4+1] = 71 + (unsigned char)(random * 32);
78 data[i*4+0] = 23 + (unsigned char)(random * 16);
81 for (i = 8192;i < 32768;i++)
83 random = lhrandom (0.0,1.0);
84 data[i*4+3] = data[i*4+0];
85 data[i*4+2] = 95 + (unsigned char)(random * 64);
86 data[i*4+1] = 95 + (unsigned char)(random * 64);
87 data[i*4+0] = 95 + (unsigned char)(random * 64);
90 for (i = 32768;i < 40960;i++)
92 random = lhrandom (0.0,1.0);
93 data[i*4+3] = data[i*4+0];
94 data[i*4+2] = 83 + (unsigned char)(random * 64);
95 data[i*4+1] = 71 + (unsigned char)(random * 32);
96 data[i*4+0] = 23 + (unsigned char)(random * 16);
99 for (i = 40960;i < 65536;i++)
101 random = lhrandom (0.0,1.0);
102 data[i*4+3] = data[i*4+0];
103 data[i*4+2] = 96 + (unsigned char)(random * 64);
104 data[i*4+1] = 43 + (unsigned char)(random * 32);
105 data[i*4+0] = 27 + (unsigned char)(random * 32);
109 Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, data);
112 tex = R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, data, TEXTYPE_BGRA, TEXF_ALPHA, -1, NULL);
117 static rtexture_t *draw_generateditherpattern(void)
120 unsigned char pixels[8][8];
121 for (y = 0;y < 8;y++)
122 for (x = 0;x < 8;x++)
123 pixels[y][x] = ((x^y) & 4) ? 254 : 0;
124 return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST, -1, palette_bgra_transparent);
127 typedef struct embeddedpic_s
136 static const embeddedpic_t embeddedpics[] =
139 "gfx/prydoncursor001", 16, 16,
158 "ui/mousepointer", 16, 16,
177 "gfx/crosshair1", 16, 16,
196 "gfx/crosshair2", 16, 16,
215 "gfx/crosshair3", 16, 16,
234 "gfx/crosshair4", 16, 16,
253 "gfx/crosshair5", 8, 8,
264 "gfx/crosshair6", 2, 2,
269 "gfx/crosshair7", 16, 16,
290 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
292 const embeddedpic_t *p;
293 for (p = embeddedpics;p->name;p++)
294 if (!strcmp(name, p->name))
295 return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA, -1, palette_bgra_embeddedpic);
296 if (!strcmp(name, "gfx/conchars"))
297 return draw_generateconchars();
298 if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
299 return draw_generateditherpattern();
301 Con_DPrintf("Draw_CachePic: failed to load %s\n", name);
302 return r_texture_notexture;
312 // FIXME: move this to client somehow
313 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
316 unsigned char *pixels = NULL;
319 unsigned char *lmpdata;
320 char lmpname[MAX_QPATH];
323 qboolean ddshasalpha;
324 float ddsavgcolor[4];
325 qboolean loaded = false;
327 texflags = TEXF_ALPHA;
328 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
329 texflags |= TEXF_CLAMP;
330 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer && gl_texturecompression.integer)
331 texflags |= TEXF_COMPRESS;
333 // check whether the picture has already been cached
334 crc = CRC_Block((unsigned char *)path, strlen(path));
335 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
336 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
338 if (!strcmp (path, pic->name))
340 // if it was created (or replaced) by Draw_NewPic, just return it
341 if(pic->flags & CACHEPICFLAG_NEWPIC)
343 if (!((pic->texflags ^ texflags) & ~(TEXF_COMPRESS))) // ignore TEXF_COMPRESS when comparing, because fallback pics remove the flag
345 if(!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
348 pic->autoload = false; // persist it
350 goto reload; // load it below, and then persist
357 if (numcachepics == MAX_CACHED_PICS)
359 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
360 // FIXME: support NULL in callers?
361 return cachepics; // return the first one
363 pic = cachepics + (numcachepics++);
364 strlcpy (pic->name, path, sizeof(pic->name));
366 pic->chain = cachepichash[hashkey];
367 cachepichash[hashkey] = pic;
370 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
371 pic->flags = cachepicflags;
372 pic->tex = CL_GetDynTexture( path );
373 // if so, set the width/height, too
375 pic->width = R_TextureWidth(pic->tex);
376 pic->height = R_TextureHeight(pic->tex);
377 // we're done now (early-out)
381 pic->hasalpha = true; // assume alpha unless we know it has none
382 pic->texflags = texflags;
383 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
384 pic->lastusedframe = draw_frame;
386 // load a high quality image from disk if possible
387 if (!loaded && r_texture_dds_load.integer != 0 && (pic->tex = R_LoadTextureDDSFile(drawtexturepool, va("dds/%s.dds", pic->name), pic->texflags, &ddshasalpha, ddsavgcolor, 0)))
389 // note this loads even if autoload is true, otherwise we can't get the width/height
391 pic->hasalpha = ddshasalpha;
392 pic->width = R_TextureWidth(pic->tex);
393 pic->height = R_TextureHeight(pic->tex);
395 if (!loaded && ((pixels = loadimagepixelsbgra(pic->name, false, true, false, NULL)) || (!strncmp(pic->name, "gfx/", 4) && (pixels = loadimagepixelsbgra(pic->name+4, false, true, false, NULL)))))
398 pic->hasalpha = false;
399 if (pic->texflags & TEXF_ALPHA)
401 for (j = 3;j < image_width * image_height * 4;j += 4)
405 pic->hasalpha = true;
411 pic->width = image_width;
412 pic->height = image_height;
415 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, image_width, image_height, pixels, vid.sRGB2D ? TEXTYPE_SRGB_BGRA : TEXTYPE_BGRA, pic->texflags & (pic->hasalpha ? ~0 : ~TEXF_ALPHA), -1, NULL);
417 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
418 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
424 pic->autoload = false;
425 // never compress the fallback images
426 pic->texflags &= ~TEXF_COMPRESS;
429 // now read the low quality version (wad or lmp file), and take the pic
430 // size from that even if we don't upload the texture, this way the pics
431 // show up the right size in the menu even if they were replaced with
432 // higher or lower resolution versions
433 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", pic->name);
434 if (!strncmp(pic->name, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
436 if (developer_loading.integer)
437 Con_Printf("loading lump \"%s\"\n", pic->name);
441 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
442 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
443 // if no high quality replacement image was found, upload the original low quality texture
447 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, pic->width, pic->height, lmpdata + 8, vid.sRGB2D ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
452 else if ((lmpdata = W_GetLumpName (pic->name + 4)))
454 if (developer_loading.integer)
455 Con_Printf("loading gfx.wad lump \"%s\"\n", pic->name + 4);
457 if (!strcmp(pic->name, "gfx/conchars"))
459 // conchars is a raw image and with color 0 as transparent instead of 255
462 // if no high quality replacement image was found, upload the original low quality texture
466 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, 128, 128, lmpdata, vid.sRGB2D != 0 ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
471 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
472 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
473 // if no high quality replacement image was found, upload the original low quality texture
477 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, pic->width, pic->height, lmpdata + 8, vid.sRGB2D != 0 ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
489 // if it's not found on disk, generate an image
490 pic->tex = draw_generatepic(pic->name, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
491 pic->width = R_TextureWidth(pic->tex);
492 pic->height = R_TextureHeight(pic->tex);
498 cachepic_t *Draw_CachePic (const char *path)
500 return Draw_CachePic_Flags (path, 0); // default to persistent!
503 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
505 if (pic->autoload && !pic->tex)
507 if (pic->tex == NULL && r_texture_dds_load.integer != 0)
509 qboolean ddshasalpha;
510 float ddsavgcolor[4];
511 pic->tex = R_LoadTextureDDSFile(drawtexturepool, va("dds/%s.dds", pic->name), pic->texflags, &ddshasalpha, ddsavgcolor, 0);
513 if (pic->tex == NULL)
515 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, vid.sRGB2D);
517 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
518 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
521 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
523 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, vid.sRGB2D);
525 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
526 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
529 if (pic->tex == NULL)
530 pic->tex = draw_generatepic(pic->name, true);
532 pic->lastusedframe = draw_frame;
536 void Draw_Frame(void)
540 static double nextpurgetime;
541 if (nextpurgetime > realtime)
543 nextpurgetime = realtime + 0.05;
544 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
546 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
548 R_FreeTexture(pic->tex);
555 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
560 crc = CRC_Block((unsigned char *)picname, strlen(picname));
561 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
562 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
563 if (!strcmp (picname, pic->name))
568 if (pic->flags == CACHEPICFLAG_NEWPIC && pic->tex && pic->width == width && pic->height == height)
570 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, 0, width, height, 1);
578 if (numcachepics == MAX_CACHED_PICS)
580 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
581 // FIXME: support NULL in callers?
582 return cachepics; // return the first one
584 pic = cachepics + (numcachepics++);
585 strlcpy (pic->name, picname, sizeof(pic->name));
587 pic->chain = cachepichash[hashkey];
588 cachepichash[hashkey] = pic;
592 pic->flags = CACHEPICFLAG_NEWPIC; // disable texflags checks in Draw_CachePic
594 pic->height = height;
596 R_FreeTexture(pic->tex);
597 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0), -1, NULL);
601 void Draw_FreePic(const char *picname)
606 // this doesn't really free the pic, but does free it's texture
607 crc = CRC_Block((unsigned char *)picname, strlen(picname));
608 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
609 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
611 if (!strcmp (picname, pic->name) && pic->tex)
613 R_FreeTexture(pic->tex);
622 static float snap_to_pixel_x(float x, float roundUpAt);
623 extern int con_linewidth; // to force rewrapping
624 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
628 char widthfile[MAX_QPATH];
630 fs_offset_t widthbufsize;
632 if(override || !fnt->texpath[0])
634 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
635 // load the cvars when the font is FIRST loader
636 fnt->settings.scale = scale;
637 fnt->settings.voffset = voffset;
638 fnt->settings.antialias = r_font_antialias.integer;
639 fnt->settings.hinting = r_font_hinting.integer;
640 fnt->settings.outline = r_font_postprocess_outline.value;
641 fnt->settings.blur = r_font_postprocess_blur.value;
642 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
643 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
644 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
647 if (fnt->settings.scale <= 0)
648 fnt->settings.scale = 1;
650 if(drawtexturepool == NULL)
651 return; // before gl_draw_start, so will be loaded later
655 // clear freetype font
656 Font_UnloadFont(fnt->ft2);
661 if(fnt->req_face != -1)
663 if(!Font_LoadFont(fnt->texpath, fnt))
664 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
667 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
668 if(fnt->tex == r_texture_notexture)
670 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
672 if (!fnt->fallbacks[i][0])
674 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
675 if(fnt->tex != r_texture_notexture)
678 if(fnt->tex == r_texture_notexture)
680 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
681 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
684 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
687 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
689 // unspecified width == 1 (base width)
690 for(ch = 0; ch < 256; ++ch)
691 fnt->width_of[ch] = 1;
693 // FIXME load "name.width", if it fails, fill all with 1
694 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
696 float extraspacing = 0;
697 const char *p = widthbuf;
702 if(!COM_ParseToken_Simple(&p, false, false))
720 fnt->width_of[ch] = atof(com_token) + extraspacing;
724 if(!strcmp(com_token, "extraspacing"))
726 if(!COM_ParseToken_Simple(&p, false, false))
728 extraspacing = atof(com_token);
730 else if(!strcmp(com_token, "scale"))
732 if(!COM_ParseToken_Simple(&p, false, false))
734 fnt->settings.scale = atof(com_token);
738 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
739 if(!COM_ParseToken_Simple(&p, false, false))
751 for (i = 0; i < MAX_FONT_SIZES; ++i)
753 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
756 for(ch = 0; ch < 256; ++ch)
757 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
761 maxwidth = fnt->width_of[0];
762 for(i = 1; i < 256; ++i)
763 maxwidth = max(maxwidth, fnt->width_of[i]);
764 fnt->maxwidth = maxwidth;
766 // fix up maxwidth for overlap
767 fnt->maxwidth *= fnt->settings.scale;
769 if(fnt == FONT_CONSOLE)
770 con_linewidth = -1; // rewrap console in next frame
773 extern cvar_t developer_font;
774 dp_font_t *FindFont(const char *title, qboolean allocate_new)
779 for(i = 0; i < dp_fonts.maxsize; ++i)
780 if(!strcmp(dp_fonts.f[i].title, title))
781 return &dp_fonts.f[i];
782 // if not found - try allocate
785 // find any font with empty title
786 for(i = 0; i < dp_fonts.maxsize; ++i)
788 if(!strcmp(dp_fonts.f[i].title, ""))
790 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
791 return &dp_fonts.f[i];
794 // if no any 'free' fonts - expand buffer
795 oldsize = dp_fonts.maxsize;
796 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
797 if (developer_font.integer)
798 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", oldsize, dp_fonts.maxsize);
799 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
800 // relink ft2 structures
801 for(i = 0; i < oldsize; ++i)
802 if (dp_fonts.f[i].ft2)
803 dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings;
804 // register a font in first expanded slot
805 strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
806 return &dp_fonts.f[oldsize];
811 static float snap_to_pixel_x(float x, float roundUpAt)
813 float pixelpos = x * vid.width / vid_conwidth.value;
814 int snap = (int) pixelpos;
815 if (pixelpos - snap >= roundUpAt) ++snap;
816 return ((float)snap * vid_conwidth.value / vid.width);
818 x = (int)(x * vid.width / vid_conwidth.value);
819 x = (x * vid_conwidth.value / vid.width);
824 static float snap_to_pixel_y(float y, float roundUpAt)
826 float pixelpos = y * vid.height / vid_conheight.value;
827 int snap = (int) pixelpos;
828 if (pixelpos - snap > roundUpAt) ++snap;
829 return ((float)snap * vid_conheight.value / vid.height);
831 y = (int)(y * vid.height / vid_conheight.value);
832 y = (y * vid_conheight.value / vid.height);
837 static void LoadFont_f(void)
841 const char *filelist, *c, *cm;
842 float sz, scale, voffset;
843 char mainfont[MAX_QPATH];
847 Con_Printf("Available font commands:\n");
848 for(i = 0; i < dp_fonts.maxsize; ++i)
849 if (dp_fonts.f[i].title[0])
850 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
851 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
852 "can specify multiple fonts and faces\n"
853 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
854 "to load face 2 of the font gfx/vera-sans and use face 1\n"
855 "of gfx/fallback as fallback font.\n"
856 "You can also specify a list of font sizes to load, like this:\n"
857 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
858 "In many cases, 8 12 16 24 32 should be a good choice.\n"
860 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
861 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
865 f = FindFont(Cmd_Argv(1), true);
868 Con_Printf("font function not found\n");
873 filelist = "gfx/conchars";
875 filelist = Cmd_Argv(2);
877 memset(f->fallbacks, 0, sizeof(f->fallbacks));
878 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
880 // first font is handled "normally"
881 c = strchr(filelist, ':');
882 cm = strchr(filelist, ',');
883 if(c && (!cm || c < cm))
884 f->req_face = atoi(c+1);
891 if(!c || (c - filelist) > MAX_QPATH)
892 strlcpy(mainfont, filelist, sizeof(mainfont));
895 memcpy(mainfont, filelist, c - filelist);
896 mainfont[c - filelist] = 0;
899 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
901 c = strchr(filelist, ',');
907 c = strchr(filelist, ':');
908 cm = strchr(filelist, ',');
909 if(c && (!cm || c < cm))
910 f->fallback_faces[i] = atoi(c+1);
913 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
916 if(!c || (c-filelist) > MAX_QPATH)
918 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
922 memcpy(f->fallbacks[i], filelist, c - filelist);
923 f->fallbacks[i][c - filelist] = 0;
927 // for now: by default load only one size: the default size
929 for(i = 1; i < MAX_FONT_SIZES; ++i)
930 f->req_sizes[i] = -1;
936 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
939 if (!strcmp(Cmd_Argv(i), "scale"))
943 scale = atof(Cmd_Argv(i));
946 if (!strcmp(Cmd_Argv(i), "voffset"))
950 voffset = atof(Cmd_Argv(i));
955 continue; // no slot for other sizes
957 // parse one of sizes
958 sz = atof(Cmd_Argv(i));
959 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
961 // search for duplicated sizes
963 for (j=0; j<sizes; j++)
964 if (f->req_sizes[j] == sz)
967 continue; // sz already in req_sizes, don't add it again
969 if (sizes == MAX_FONT_SIZES)
971 Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
975 f->req_sizes[sizes] = sz;
981 LoadFont(true, mainfont, f, scale, voffset);
989 static void gl_draw_start(void)
992 drawtexturepool = R_AllocTexturePool();
995 memset(cachepichash, 0, sizeof(cachepichash));
999 // load default font textures
1000 for(i = 0; i < dp_fonts.maxsize; ++i)
1001 if (dp_fonts.f[i].title[0])
1002 LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
1004 // draw the loading screen so people have something to see in the newly opened window
1005 SCR_UpdateLoadingScreen(true);
1008 static void gl_draw_shutdown(void)
1012 R_FreeTexturePool(&drawtexturepool);
1015 memset(cachepichash, 0, sizeof(cachepichash));
1018 static void gl_draw_newmap(void)
1023 void GL_Draw_Init (void)
1027 Cvar_RegisterVariable(&r_font_postprocess_blur);
1028 Cvar_RegisterVariable(&r_font_postprocess_outline);
1029 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
1030 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
1031 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
1032 Cvar_RegisterVariable(&r_font_hinting);
1033 Cvar_RegisterVariable(&r_font_antialias);
1034 Cvar_RegisterVariable(&r_textshadow);
1035 Cvar_RegisterVariable(&r_textbrightness);
1036 Cvar_RegisterVariable(&r_textcontrast);
1038 // allocate fonts storage
1039 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
1040 dp_fonts.maxsize = MAX_FONTS;
1041 dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
1042 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
1044 // assign starting font names
1045 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
1046 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
1047 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
1048 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
1049 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
1050 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
1051 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
1052 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
1053 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
1054 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
1055 if(!FONT_USER(i)->title[0])
1056 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
1058 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
1059 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
1062 static void _DrawQ_Setup(void)
1064 r_viewport_t viewport;
1065 if (r_refdef.draw2dstage == 1)
1067 r_refdef.draw2dstage = 1;
1069 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);
1070 R_Mesh_ResetRenderTargets();
1071 R_SetViewport(&viewport);
1072 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
1073 GL_DepthFunc(GL_LEQUAL);
1074 GL_PolygonOffset(0,0);
1075 GL_CullFace(GL_NONE);
1076 R_EntityMatrix(&identitymatrix);
1078 GL_DepthRange(0, 1);
1079 GL_PolygonOffset(0, 0);
1080 GL_DepthTest(false);
1084 qboolean r_draw2d_force = false;
1085 void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1089 if(!r_draw2d.integer && !r_draw2d_force)
1091 DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1093 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1095 if(flags == DRAWFLAG_ADDITIVE)
1097 GL_DepthMask(false);
1098 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1100 else if(flags == DRAWFLAG_MODULATE)
1102 GL_DepthMask(false);
1103 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1105 else if(flags == DRAWFLAG_2XMODULATE)
1107 GL_DepthMask(false);
1108 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1110 else if(flags == DRAWFLAG_SCREEN)
1112 GL_DepthMask(false);
1113 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1117 GL_DepthMask(false);
1118 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1123 GL_BlendFunc(GL_ONE, GL_ZERO);
1127 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1131 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1132 if(!r_draw2d.integer && !r_draw2d_force)
1135 // R_Mesh_ResetTextureState();
1136 floats[12] = 0.0f;floats[13] = 0.0f;
1137 floats[14] = 1.0f;floats[15] = 0.0f;
1138 floats[16] = 1.0f;floats[17] = 1.0f;
1139 floats[18] = 0.0f;floats[19] = 1.0f;
1140 floats[20] = floats[24] = floats[28] = floats[32] = red;
1141 floats[21] = floats[25] = floats[29] = floats[33] = green;
1142 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1143 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1149 height = pic->height;
1150 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1153 // AK07: lets be texel correct on the corners
1155 float horz_offset = 0.5f / pic->width;
1156 float vert_offset = 0.5f / pic->height;
1158 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1159 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1160 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1161 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1166 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1168 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1169 floats[0] = floats[9] = x;
1170 floats[1] = floats[4] = y;
1171 floats[3] = floats[6] = x + width;
1172 floats[7] = floats[10] = y + height;
1174 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1175 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1178 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)
1181 float af = DEG2RAD(-angle); // forward
1182 float ar = DEG2RAD(-angle + 90); // right
1183 float sinaf = sin(af);
1184 float cosaf = cos(af);
1185 float sinar = sin(ar);
1186 float cosar = cos(ar);
1188 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1189 if(!r_draw2d.integer && !r_draw2d_force)
1192 // R_Mesh_ResetTextureState();
1198 height = pic->height;
1199 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1202 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1204 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1207 floats[0] = x - cosaf*org_x - cosar*org_y;
1208 floats[1] = y - sinaf*org_x - sinar*org_y;
1211 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1212 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1215 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1216 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1219 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1220 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1222 floats[12] = 0.0f;floats[13] = 0.0f;
1223 floats[14] = 1.0f;floats[15] = 0.0f;
1224 floats[16] = 1.0f;floats[17] = 1.0f;
1225 floats[18] = 0.0f;floats[19] = 1.0f;
1226 floats[20] = floats[24] = floats[28] = floats[32] = red;
1227 floats[21] = floats[25] = floats[29] = floats[33] = green;
1228 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1229 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1231 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1232 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1235 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1239 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1240 if(!r_draw2d.integer && !r_draw2d_force)
1243 // R_Mesh_ResetTextureState();
1244 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1246 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1247 floats[0] = floats[9] = x;
1248 floats[1] = floats[4] = y;
1249 floats[3] = floats[6] = x + width;
1250 floats[7] = floats[10] = y + height;
1251 floats[12] = 0.0f;floats[13] = 0.0f;
1252 floats[14] = 1.0f;floats[15] = 0.0f;
1253 floats[16] = 1.0f;floats[17] = 1.0f;
1254 floats[18] = 0.0f;floats[19] = 1.0f;
1255 floats[20] = floats[24] = floats[28] = floats[32] = red;
1256 floats[21] = floats[25] = floats[29] = floats[33] = green;
1257 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1258 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1260 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1261 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1264 /// color tag printing
1265 static const vec4_t string_colors[] =
1268 // LordHavoc: why on earth is cyan before magenta in Quake3?
1269 // LordHavoc: note: Doom3 uses white for [0] and [7]
1270 {0.0, 0.0, 0.0, 1.0}, // black
1271 {1.0, 0.0, 0.0, 1.0}, // red
1272 {0.0, 1.0, 0.0, 1.0}, // green
1273 {1.0, 1.0, 0.0, 1.0}, // yellow
1274 {0.0, 0.0, 1.0, 1.0}, // blue
1275 {0.0, 1.0, 1.0, 1.0}, // cyan
1276 {1.0, 0.0, 1.0, 1.0}, // magenta
1277 {1.0, 1.0, 1.0, 1.0}, // white
1278 // [515]'s BX_COLOREDTEXT extension
1279 {1.0, 1.0, 1.0, 0.5}, // half transparent
1280 {0.5, 0.5, 0.5, 1.0} // half brightness
1281 // Black's color table
1282 //{1.0, 1.0, 1.0, 1.0},
1283 //{1.0, 0.0, 0.0, 1.0},
1284 //{0.0, 1.0, 0.0, 1.0},
1285 //{0.0, 0.0, 1.0, 1.0},
1286 //{1.0, 1.0, 0.0, 1.0},
1287 //{0.0, 1.0, 1.0, 1.0},
1288 //{1.0, 0.0, 1.0, 1.0},
1289 //{0.1, 0.1, 0.1, 1.0}
1292 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1294 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1296 float C = r_textcontrast.value;
1297 float B = r_textbrightness.value;
1298 if (colorindex & 0x10000) // that bit means RGB color
1300 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1301 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1302 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1303 color[3] = (colorindex & 0xf) / 15.0;
1306 Vector4Copy(string_colors[colorindex], color);
1307 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1310 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1311 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1315 // NOTE: this function always draws exactly one character if maxwidth <= 0
1316 float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *maxlen, float w, float h, float sw, float sh, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
1318 const char *text_start = text;
1319 int colorindex = STRING_COLOR_DEFAULT;
1322 Uchar ch, mapch, nextch;
1323 Uchar prevch = 0; // used for kerning
1328 ft2_font_map_t *fontmap = NULL;
1329 ft2_font_map_t *map = NULL;
1330 //ft2_font_map_t *prevmap = NULL;
1331 ft2_font_t *ft2 = fnt->ft2;
1333 qboolean snap = true;
1334 qboolean least_one = false;
1335 float dw; // display w
1336 //float dh; // display h
1337 const float *width_of;
1344 // do this in the end
1345 w *= fnt->settings.scale;
1346 h *= fnt->settings.scale;
1348 // find the most fitting size:
1352 map_index = Font_IndexForSize(ft2, h, &w, &h);
1354 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1355 fontmap = Font_MapForIndex(ft2, map_index);
1364 if (!outcolor || *outcolor == -1)
1365 colorindex = STRING_COLOR_DEFAULT;
1367 colorindex = *outcolor;
1369 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1370 // ftbase_x = snap_to_pixel_x(0);
1375 maxwidth = -maxwidth;
1379 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1382 width_of = fontmap->width_of;
1384 width_of = fnt->width_of;
1386 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1389 nextch = ch = u8_getnchar(text, &text, bytes_left);
1390 i = text - text_start;
1393 if (ch == ' ' && !fontmap)
1395 if(!least_one || i0) // never skip the first character
1396 if(x + width_of[(int) ' '] * dw > maxwidth)
1399 break; // oops, can't draw this
1401 x += width_of[(int) ' '] * dw;
1404 // i points to the char after ^
1405 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1407 ch = *text; // colors are ascii, so no u8_ needed
1408 if (ch <= '9' && ch >= '0') // ^[0-9] found
1410 colorindex = ch - '0';
1415 // i points to the char after ^...
1416 // i+3 points to 3 in ^x123
1417 // i+3 == *maxlen would mean that char is missing
1418 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1420 // building colorindex...
1421 ch = tolower(text[1]);
1422 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1423 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1424 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1425 else tempcolorindex = 0;
1428 ch = tolower(text[2]);
1429 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1430 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1431 else tempcolorindex = 0;
1434 ch = tolower(text[3]);
1435 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1436 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1437 else tempcolorindex = 0;
1440 colorindex = tempcolorindex | 0xf;
1441 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1449 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1458 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1465 map = ft2_oldstyle_map;
1467 if(!least_one || i0) // never skip the first character
1468 if(x + width_of[ch] * dw > maxwidth)
1471 break; // oops, can't draw this
1473 x += width_of[ch] * dw;
1475 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1477 map = FontMap_FindForChar(fontmap, ch);
1480 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1486 mapch = ch - map->start;
1487 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1489 x += map->glyphs[mapch].advance_x * dw;
1498 *outcolor = colorindex;
1503 float DrawQ_Color[4];
1504 float DrawQ_String_Scale(float startx, float starty, const char *text, size_t maxlen, float w, float h, float sw, float sh, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt)
1506 int shadow, colorindex = STRING_COLOR_DEFAULT;
1508 float x = startx, y, s, t, u, v, thisw;
1509 float *av, *at, *ac;
1511 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1512 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1513 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1514 Uchar ch, mapch, nextch;
1515 Uchar prevch = 0; // used for kerning
1518 //ft2_font_map_t *prevmap = NULL; // the previous map
1519 ft2_font_map_t *map = NULL; // the currently used map
1520 ft2_font_map_t *fontmap = NULL; // the font map for the size
1522 const char *text_start = text;
1524 ft2_font_t *ft2 = fnt->ft2;
1525 qboolean snap = true;
1529 const float *width_of;
1532 tw = R_TextureWidth(fnt->tex);
1533 th = R_TextureHeight(fnt->tex);
1541 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1542 w *= fnt->settings.scale;
1543 h *= fnt->settings.scale;
1548 map_index = Font_IndexForSize(ft2, h, &w, &h);
1550 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1551 fontmap = Font_MapForIndex(ft2, map_index);
1557 // draw the font at its baseline when using freetype
1559 ftbase_y = dh * (4.5/6.0);
1564 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1565 if(!r_draw2d.integer && !r_draw2d_force)
1566 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1568 // R_Mesh_ResetTextureState();
1570 R_Mesh_TexBind(0, fnt->tex);
1571 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1578 //ftbase_x = snap_to_pixel_x(ftbase_x);
1581 startx = snap_to_pixel_x(startx, 0.4);
1582 starty = snap_to_pixel_y(starty, 0.4);
1583 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1586 pix_x = vid.width / vid_conwidth.value;
1587 pix_y = vid.height / vid_conheight.value;
1590 width_of = fontmap->width_of;
1592 width_of = fnt->width_of;
1594 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1599 if (!outcolor || *outcolor == -1)
1600 colorindex = STRING_COLOR_DEFAULT;
1602 colorindex = *outcolor;
1604 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1611 x += r_textshadow.value * vid.width / vid_conwidth.value;
1612 y += r_textshadow.value * vid.height / vid_conheight.value;
1615 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1617 nextch = ch = u8_getnchar(text, &text, bytes_left);
1618 i = text - text_start;
1621 if (ch == ' ' && !fontmap)
1623 x += width_of[(int) ' '] * dw;
1626 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1628 ch = *text; // colors are ascii, so no u8_ needed
1629 if (ch <= '9' && ch >= '0') // ^[0-9] found
1631 colorindex = ch - '0';
1632 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1637 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1639 // building colorindex...
1640 ch = tolower(text[1]);
1641 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1642 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1643 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1644 else tempcolorindex = 0;
1647 ch = tolower(text[2]);
1648 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1649 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1650 else tempcolorindex = 0;
1653 ch = tolower(text[3]);
1654 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1655 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1656 else tempcolorindex = 0;
1659 colorindex = tempcolorindex | 0xf;
1660 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1661 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1662 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1670 else if (ch == STRING_COLOR_TAG)
1679 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1680 // this way we don't need to rebind fnt->tex for every old-style character
1681 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1684 x += 1.0/pix_x * r_textshadow.value;
1685 y += 1.0/pix_y * r_textshadow.value;
1687 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1695 if (map != ft2_oldstyle_map)
1699 // switching from freetype to non-freetype rendering
1700 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1701 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1707 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1708 map = ft2_oldstyle_map;
1712 //num = (unsigned char) text[i];
1713 //thisw = fnt->width_of[num];
1714 thisw = fnt->width_of[ch];
1715 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1716 s = (ch & 15)*0.0625f + (0.5f / tw);
1717 t = (ch >> 4)*0.0625f + (0.5f / th);
1718 u = 0.0625f * thisw - (1.0f / tw);
1719 v = 0.0625f - (1.0f / th);
1720 ac[ 0] = DrawQ_Color[0];ac[ 1] = DrawQ_Color[1];ac[ 2] = DrawQ_Color[2];ac[ 3] = DrawQ_Color[3];
1721 ac[ 4] = DrawQ_Color[0];ac[ 5] = DrawQ_Color[1];ac[ 6] = DrawQ_Color[2];ac[ 7] = DrawQ_Color[3];
1722 ac[ 8] = DrawQ_Color[0];ac[ 9] = DrawQ_Color[1];ac[10] = DrawQ_Color[2];ac[11] = DrawQ_Color[3];
1723 ac[12] = DrawQ_Color[0];ac[13] = DrawQ_Color[1];ac[14] = DrawQ_Color[2];ac[15] = DrawQ_Color[3];
1724 at[ 0] = s ; at[ 1] = t ;
1725 at[ 2] = s+u ; at[ 3] = t ;
1726 at[ 4] = s+u ; at[ 5] = t+v ;
1727 at[ 6] = s ; at[ 7] = t+v ;
1728 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1729 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1730 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1731 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1736 if (batchcount >= QUADELEMENTS_MAXQUADS)
1738 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1739 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1745 x += width_of[ch] * dw;
1747 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1749 // new charmap - need to render
1752 // we need a different character map, render what we currently have:
1753 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1754 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1761 map = FontMap_FindForChar(fontmap, ch);
1764 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1771 // this shouldn't happen
1776 R_SetupShader_Generic(map->pic->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1779 mapch = ch - map->start;
1780 thisw = map->glyphs[mapch].advance_x;
1784 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1791 ac[ 0] = DrawQ_Color[0]; ac[ 1] = DrawQ_Color[1]; ac[ 2] = DrawQ_Color[2]; ac[ 3] = DrawQ_Color[3];
1792 ac[ 4] = DrawQ_Color[0]; ac[ 5] = DrawQ_Color[1]; ac[ 6] = DrawQ_Color[2]; ac[ 7] = DrawQ_Color[3];
1793 ac[ 8] = DrawQ_Color[0]; ac[ 9] = DrawQ_Color[1]; ac[10] = DrawQ_Color[2]; ac[11] = DrawQ_Color[3];
1794 ac[12] = DrawQ_Color[0]; ac[13] = DrawQ_Color[1]; ac[14] = DrawQ_Color[2]; ac[15] = DrawQ_Color[3];
1795 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1796 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1797 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1798 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1799 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1800 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1801 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1802 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1811 if (batchcount >= QUADELEMENTS_MAXQUADS)
1813 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1814 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1827 x -= 1.0/pix_x * r_textshadow.value;
1828 y -= 1.0/pix_y * r_textshadow.value;
1834 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1835 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1839 *outcolor = colorindex;
1841 // note: this relies on the proper text (not shadow) being drawn last
1845 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, const dp_font_t *fnt)
1847 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1850 float DrawQ_TextWidth_UntilWidth_TrackColors(const char *text, size_t *maxlen, float w, float h, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
1852 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1855 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1857 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1860 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1862 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1867 // no ^xrgb management
1868 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1870 int color, numchars = 0;
1871 char *outputend2c = output2c + maxoutchars - 2;
1872 if (!outcolor || *outcolor == -1)
1873 color = STRING_COLOR_DEFAULT;
1877 maxreadchars = 1<<30;
1878 textend = text + maxreadchars;
1879 while (text != textend && *text)
1881 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1883 if (text[1] == STRING_COLOR_TAG)
1885 else if (text[1] >= '0' && text[1] <= '9')
1887 color = text[1] - '0';
1892 if (output2c >= outputend2c)
1894 *output2c++ = *text++;
1895 *output2c++ = color;
1898 output2c[0] = output2c[1] = 0;
1905 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)
1909 _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1910 if(!r_draw2d.integer && !r_draw2d_force)
1913 // R_Mesh_ResetTextureState();
1919 height = pic->height;
1920 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1923 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1925 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1926 floats[0] = floats[9] = x;
1927 floats[1] = floats[4] = y;
1928 floats[3] = floats[6] = x + width;
1929 floats[7] = floats[10] = y + height;
1930 floats[12] = s1;floats[13] = t1;
1931 floats[14] = s2;floats[15] = t2;
1932 floats[16] = s4;floats[17] = t4;
1933 floats[18] = s3;floats[19] = t3;
1934 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1935 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1936 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1937 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1939 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1940 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1943 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1947 if(!r_draw2d.integer && !r_draw2d_force)
1949 DrawQ_ProcessDrawFlag(flags, hasalpha);
1951 // R_Mesh_ResetTextureState();
1952 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1954 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1955 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1958 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1962 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1963 if(!r_draw2d.integer && !r_draw2d_force)
1967 switch(vid.renderpath)
1969 case RENDERPATH_GL11:
1970 case RENDERPATH_GL13:
1971 case RENDERPATH_GL20:
1974 qglBegin(GL_LINE_LOOP);
1975 for (num = 0;num < mesh->num_vertices;num++)
1977 if (mesh->data_color4f)
1978 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]);
1979 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1985 case RENDERPATH_D3D9:
1986 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1988 case RENDERPATH_D3D10:
1989 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1991 case RENDERPATH_D3D11:
1992 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1994 case RENDERPATH_SOFT:
1995 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1997 case RENDERPATH_GLES1:
1998 case RENDERPATH_GLES2:
1999 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2004 //[515]: this is old, delete
2005 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
2007 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
2008 if(!r_draw2d.integer && !r_draw2d_force)
2011 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
2013 switch(vid.renderpath)
2015 case RENDERPATH_GL11:
2016 case RENDERPATH_GL13:
2017 case RENDERPATH_GL20:
2021 //qglLineWidth(width);CHECKGLERROR
2023 GL_Color(r,g,b,alpha);
2026 qglVertex2f(x1, y1);
2027 qglVertex2f(x2, y2);
2032 case RENDERPATH_D3D9:
2033 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2035 case RENDERPATH_D3D10:
2036 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2038 case RENDERPATH_D3D11:
2039 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2041 case RENDERPATH_SOFT:
2042 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2044 case RENDERPATH_GLES1:
2045 case RENDERPATH_GLES2:
2046 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2051 void DrawQ_Lines (float width, int numlines, const float *vertex3f, const float *color4f, int flags)
2054 qboolean hasalpha = false;
2055 for (i = 0;i < numlines*2;i++)
2056 if (color4f[i*4+3] < 1.0f)
2059 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, hasalpha ? 0.5f : 1.0f);
2061 if(!r_draw2d.integer && !r_draw2d_force)
2064 switch(vid.renderpath)
2066 case RENDERPATH_GL11:
2067 case RENDERPATH_GL13:
2068 case RENDERPATH_GL20:
2071 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
2073 //qglLineWidth(width);CHECKGLERROR
2076 R_Mesh_PrepareVertices_Generic_Arrays(numlines*2, vertex3f, color4f, NULL);
2077 qglDrawArrays(GL_LINES, 0, numlines*2);
2080 case RENDERPATH_D3D9:
2081 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2083 case RENDERPATH_D3D10:
2084 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2086 case RENDERPATH_D3D11:
2087 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2089 case RENDERPATH_SOFT:
2090 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2092 case RENDERPATH_GLES1:
2093 case RENDERPATH_GLES2:
2094 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2099 void DrawQ_SetClipArea(float x, float y, float width, float height)
2104 // We have to convert the con coords into real coords
2105 // OGL uses top to bottom
2106 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
2107 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
2108 iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
2109 ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
2110 switch(vid.renderpath)
2112 case RENDERPATH_GL11:
2113 case RENDERPATH_GL13:
2114 case RENDERPATH_GL20:
2115 case RENDERPATH_GLES1:
2116 case RENDERPATH_GLES2:
2117 case RENDERPATH_SOFT:
2118 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
2120 case RENDERPATH_D3D9:
2121 GL_Scissor(ix, iy, iw, ih);
2123 case RENDERPATH_D3D10:
2124 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2126 case RENDERPATH_D3D11:
2127 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2131 GL_ScissorTest(true);
2134 void DrawQ_ResetClipArea(void)
2137 GL_ScissorTest(false);
2140 void DrawQ_Finish(void)
2142 r_refdef.draw2dstage = 0;
2145 void DrawQ_RecalcView(void)
2147 if(r_refdef.draw2dstage)
2148 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2151 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2152 void R_DrawGamma(void)
2155 switch(vid.renderpath)
2157 case RENDERPATH_GL20:
2158 case RENDERPATH_D3D9:
2159 case RENDERPATH_D3D10:
2160 case RENDERPATH_D3D11:
2161 case RENDERPATH_GLES2:
2162 if (vid_usinghwgamma || v_glslgamma.integer)
2165 case RENDERPATH_GL11:
2166 case RENDERPATH_GL13:
2167 if (vid_usinghwgamma)
2170 case RENDERPATH_GLES1:
2171 case RENDERPATH_SOFT:
2174 // all the blends ignore depth
2175 // R_Mesh_ResetTextureState();
2176 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, true, true);
2178 GL_DepthRange(0, 1);
2179 GL_PolygonOffset(0, 0);
2180 GL_DepthTest(false);
2182 // interpretation of brightness and contrast:
2183 // color range := brightness .. (brightness + contrast)
2184 // i.e. "c *= contrast; c += brightness"
2185 // plausible values for brightness thus range from -contrast to 1
2187 // apply pre-brightness (subtractive brightness, for where contrast was >= 1)
2188 if (vid.support.ext_blend_subtract)
2190 if (v_color_enable.integer)
2192 c[0] = -v_color_black_r.value / v_color_white_r.value;
2193 c[1] = -v_color_black_g.value / v_color_white_g.value;
2194 c[2] = -v_color_black_b.value / v_color_white_b.value;
2197 c[0] = c[1] = c[2] = -v_brightness.value / v_contrast.value;
2198 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2200 // need SUBTRACTIVE blending to do this!
2201 GL_BlendEquationSubtract(true);
2202 GL_BlendFunc(GL_ONE, GL_ONE);
2203 GL_Color(c[0], c[1], c[2], 1);
2204 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2205 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2206 GL_BlendEquationSubtract(false);
2211 if (v_color_enable.integer)
2213 c[0] = v_color_white_r.value;
2214 c[1] = v_color_white_g.value;
2215 c[2] = v_color_white_b.value;
2218 c[0] = c[1] = c[2] = v_contrast.value;
2219 if (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2221 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2222 while (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2225 cc[0] = bound(0, c[0] - 1, 1);
2226 cc[1] = bound(0, c[1] - 1, 1);
2227 cc[2] = bound(0, c[2] - 1, 1);
2228 GL_Color(cc[0], cc[1], cc[2], 1);
2229 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2230 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2236 if (c[0] <= 0.997f || c[1] <= 0.997f || c[2] <= 0.997f)
2238 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
2239 GL_Color(c[0], c[1], c[2], 1);
2240 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2241 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2244 // apply post-brightness (additive brightness, for where contrast was <= 1)
2245 if (v_color_enable.integer)
2247 c[0] = v_color_black_r.value;
2248 c[1] = v_color_black_g.value;
2249 c[2] = v_color_black_b.value;
2252 c[0] = c[1] = c[2] = v_brightness.value;
2253 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2255 GL_BlendFunc(GL_ONE, GL_ONE);
2256 GL_Color(c[0], c[1], c[2], 1);
2257 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2258 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);