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;
328 texflags = TEXF_ALPHA;
329 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
330 texflags |= TEXF_CLAMP;
331 if (cachepicflags & CACHEPICFLAG_MIPMAP)
332 texflags |= TEXF_MIPMAP;
333 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer && gl_texturecompression.integer)
334 texflags |= TEXF_COMPRESS;
336 // check whether the picture has already been cached
337 crc = CRC_Block((unsigned char *)path, strlen(path));
338 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
339 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
341 if (!strcmp (path, pic->name))
343 // if it was created (or replaced) by Draw_NewPic, just return it
344 if(pic->flags & CACHEPICFLAG_NEWPIC)
346 // if (!((pic->texflags ^ texflags) & ~(TEXF_COMPRESS))) // ignore TEXF_COMPRESS when comparing, because fallback pics remove the flag
347 // better don't compare texflags at all, because draw operations have no way to specify the texflags
349 if(!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
352 pic->autoload = false; // persist it
354 goto reload; // load it below, and then persist
361 if (numcachepics == MAX_CACHED_PICS)
363 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
364 // FIXME: support NULL in callers?
365 return cachepics; // return the first one
367 pic = cachepics + (numcachepics++);
368 memset(pic, 0, sizeof(*pic));
369 strlcpy (pic->name, path, sizeof(pic->name));
371 pic->chain = cachepichash[hashkey];
372 cachepichash[hashkey] = pic;
375 // TODO why does this crash?
376 if(pic->allow_free_tex && pic->tex)
377 R_PurgeTexture(pic->tex);
379 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
380 pic->flags = cachepicflags;
381 pic->tex = CL_GetDynTexture( path );
382 // if so, set the width/height, too
384 pic->allow_free_tex = false;
385 pic->width = R_TextureWidth(pic->tex);
386 pic->height = R_TextureHeight(pic->tex);
387 // we're done now (early-out)
391 pic->allow_free_tex = true;
393 pic->hasalpha = true; // assume alpha unless we know it has none
394 pic->texflags = texflags;
395 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
396 pic->lastusedframe = draw_frame;
398 // load a high quality image from disk if possible
399 if (!loaded && r_texture_dds_load.integer != 0 && (pic->tex = R_LoadTextureDDSFile(drawtexturepool, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), vid.sRGB2D, pic->texflags, &ddshasalpha, ddsavgcolor, 0)))
401 // note this loads even if autoload is true, otherwise we can't get the width/height
403 pic->hasalpha = ddshasalpha;
404 pic->width = R_TextureWidth(pic->tex);
405 pic->height = R_TextureHeight(pic->tex);
407 if (!loaded && ((pixels = loadimagepixelsbgra(pic->name, false, true, false, NULL)) || (!strncmp(pic->name, "gfx/", 4) && (pixels = loadimagepixelsbgra(pic->name+4, false, true, false, NULL)))))
410 pic->hasalpha = false;
411 if (pic->texflags & TEXF_ALPHA)
413 for (j = 3;j < image_width * image_height * 4;j += 4)
417 pic->hasalpha = true;
423 pic->width = image_width;
424 pic->height = image_height;
427 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);
429 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
430 R_SaveTextureDDSFile(pic->tex, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
436 pic->autoload = false;
437 // never compress the fallback images
438 pic->texflags &= ~TEXF_COMPRESS;
441 // now read the low quality version (wad or lmp file), and take the pic
442 // size from that even if we don't upload the texture, this way the pics
443 // show up the right size in the menu even if they were replaced with
444 // higher or lower resolution versions
445 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", pic->name);
446 if (!strncmp(pic->name, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
448 if (developer_loading.integer)
449 Con_Printf("loading lump \"%s\"\n", pic->name);
453 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
454 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
455 // if no high quality replacement image was found, upload the original low quality texture
459 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);
464 else if ((lmpdata = W_GetLumpName (pic->name + 4)))
466 if (developer_loading.integer)
467 Con_Printf("loading gfx.wad lump \"%s\"\n", pic->name + 4);
469 if (!strcmp(pic->name, "gfx/conchars"))
471 // conchars is a raw image and with color 0 as transparent instead of 255
474 // if no high quality replacement image was found, upload the original low quality texture
478 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, 128, 128, lmpdata, vid.sRGB2D != 0 ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
483 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
484 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
485 // if no high quality replacement image was found, upload the original low quality texture
489 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);
501 // if it's not found on disk, generate an image
502 pic->tex = draw_generatepic(pic->name, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
503 pic->width = R_TextureWidth(pic->tex);
504 pic->height = R_TextureHeight(pic->tex);
505 pic->allow_free_tex = (pic->tex != r_texture_notexture);
511 cachepic_t *Draw_CachePic (const char *path)
513 return Draw_CachePic_Flags (path, 0); // default to persistent!
516 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
519 if (pic->autoload && !pic->tex)
521 if (pic->tex == NULL && r_texture_dds_load.integer != 0)
523 qboolean ddshasalpha;
524 float ddsavgcolor[4];
525 pic->tex = R_LoadTextureDDSFile(drawtexturepool, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), vid.sRGB2D, pic->texflags, &ddshasalpha, ddsavgcolor, 0);
527 if (pic->tex == NULL)
529 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, vid.sRGB2D);
531 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
532 R_SaveTextureDDSFile(pic->tex, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
535 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
537 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, vid.sRGB2D);
539 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
540 R_SaveTextureDDSFile(pic->tex, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
543 if (pic->tex == NULL)
544 pic->tex = draw_generatepic(pic->name, true);
546 pic->lastusedframe = draw_frame;
550 void Draw_Frame(void)
554 static double nextpurgetime;
555 if (nextpurgetime > realtime)
557 nextpurgetime = realtime + 0.05;
558 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
560 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
562 R_FreeTexture(pic->tex);
569 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
574 crc = CRC_Block((unsigned char *)picname, strlen(picname));
575 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
576 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
577 if (!strcmp (picname, pic->name))
582 if (pic->flags == CACHEPICFLAG_NEWPIC && pic->tex && pic->width == width && pic->height == height)
584 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, 0, width, height, 1);
590 if (numcachepics == MAX_CACHED_PICS)
592 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
593 // FIXME: support NULL in callers?
594 return cachepics; // return the first one
596 pic = cachepics + (numcachepics++);
597 memset(pic, 0, sizeof(*pic));
598 strlcpy (pic->name, picname, sizeof(pic->name));
600 pic->chain = cachepichash[hashkey];
601 cachepichash[hashkey] = pic;
604 pic->flags = CACHEPICFLAG_NEWPIC; // disable texflags checks in Draw_CachePic
606 pic->height = height;
607 if (pic->allow_free_tex && pic->tex)
608 R_FreeTexture(pic->tex);
609 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0), -1, NULL);
613 void Draw_FreePic(const char *picname)
618 // this doesn't really free the pic, but does free it's texture
619 crc = CRC_Block((unsigned char *)picname, strlen(picname));
620 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
621 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
623 if (!strcmp (picname, pic->name) && pic->tex)
625 R_FreeTexture(pic->tex);
634 static float snap_to_pixel_x(float x, float roundUpAt);
635 extern int con_linewidth; // to force rewrapping
636 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
640 char widthfile[MAX_QPATH];
642 fs_offset_t widthbufsize;
644 if(override || !fnt->texpath[0])
646 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
647 // load the cvars when the font is FIRST loader
648 fnt->settings.scale = scale;
649 fnt->settings.voffset = voffset;
650 fnt->settings.antialias = r_font_antialias.integer;
651 fnt->settings.hinting = r_font_hinting.integer;
652 fnt->settings.outline = r_font_postprocess_outline.value;
653 fnt->settings.blur = r_font_postprocess_blur.value;
654 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
655 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
656 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
659 if (fnt->settings.scale <= 0)
660 fnt->settings.scale = 1;
662 if(drawtexturepool == NULL)
663 return; // before gl_draw_start, so will be loaded later
667 // clear freetype font
668 Font_UnloadFont(fnt->ft2);
673 if(fnt->req_face != -1)
675 if(!Font_LoadFont(fnt->texpath, fnt))
676 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
679 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
680 if(fnt->tex == r_texture_notexture)
682 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
684 if (!fnt->fallbacks[i][0])
686 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
687 if(fnt->tex != r_texture_notexture)
690 if(fnt->tex == r_texture_notexture)
692 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
693 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
696 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
699 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
701 // unspecified width == 1 (base width)
702 for(ch = 0; ch < 256; ++ch)
703 fnt->width_of[ch] = 1;
705 // FIXME load "name.width", if it fails, fill all with 1
706 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
708 float extraspacing = 0;
709 const char *p = widthbuf;
714 if(!COM_ParseToken_Simple(&p, false, false, true))
732 fnt->width_of[ch] = atof(com_token) + extraspacing;
736 if(!strcmp(com_token, "extraspacing"))
738 if(!COM_ParseToken_Simple(&p, false, false, true))
740 extraspacing = atof(com_token);
742 else if(!strcmp(com_token, "scale"))
744 if(!COM_ParseToken_Simple(&p, false, false, true))
746 fnt->settings.scale = atof(com_token);
750 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
751 if(!COM_ParseToken_Simple(&p, false, false, true))
763 for (i = 0; i < MAX_FONT_SIZES; ++i)
765 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
768 for(ch = 0; ch < 256; ++ch)
769 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
773 maxwidth = fnt->width_of[0];
774 for(i = 1; i < 256; ++i)
775 maxwidth = max(maxwidth, fnt->width_of[i]);
776 fnt->maxwidth = maxwidth;
778 // fix up maxwidth for overlap
779 fnt->maxwidth *= fnt->settings.scale;
781 if(fnt == FONT_CONSOLE)
782 con_linewidth = -1; // rewrap console in next frame
785 extern cvar_t developer_font;
786 dp_font_t *FindFont(const char *title, qboolean allocate_new)
791 for(i = 0; i < dp_fonts.maxsize; ++i)
792 if(!strcmp(dp_fonts.f[i].title, title))
793 return &dp_fonts.f[i];
794 // if not found - try allocate
797 // find any font with empty title
798 for(i = 0; i < dp_fonts.maxsize; ++i)
800 if(!strcmp(dp_fonts.f[i].title, ""))
802 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
803 return &dp_fonts.f[i];
806 // if no any 'free' fonts - expand buffer
807 oldsize = dp_fonts.maxsize;
808 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
809 if (developer_font.integer)
810 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", oldsize, dp_fonts.maxsize);
811 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
812 // relink ft2 structures
813 for(i = 0; i < oldsize; ++i)
814 if (dp_fonts.f[i].ft2)
815 dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings;
816 // register a font in first expanded slot
817 strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
818 return &dp_fonts.f[oldsize];
823 static float snap_to_pixel_x(float x, float roundUpAt)
825 float pixelpos = x * vid.width / vid_conwidth.value;
826 int snap = (int) pixelpos;
827 if (pixelpos - snap >= roundUpAt) ++snap;
828 return ((float)snap * vid_conwidth.value / vid.width);
830 x = (int)(x * vid.width / vid_conwidth.value);
831 x = (x * vid_conwidth.value / vid.width);
836 static float snap_to_pixel_y(float y, float roundUpAt)
838 float pixelpos = y * vid.height / vid_conheight.value;
839 int snap = (int) pixelpos;
840 if (pixelpos - snap > roundUpAt) ++snap;
841 return ((float)snap * vid_conheight.value / vid.height);
843 y = (int)(y * vid.height / vid_conheight.value);
844 y = (y * vid_conheight.value / vid.height);
849 static void LoadFont_f(void)
853 const char *filelist, *c, *cm;
854 float sz, scale, voffset;
855 char mainfont[MAX_QPATH];
859 Con_Printf("Available font commands:\n");
860 for(i = 0; i < dp_fonts.maxsize; ++i)
861 if (dp_fonts.f[i].title[0])
862 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
863 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
864 "can specify multiple fonts and faces\n"
865 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
866 "to load face 2 of the font gfx/vera-sans and use face 1\n"
867 "of gfx/fallback as fallback font.\n"
868 "You can also specify a list of font sizes to load, like this:\n"
869 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
870 "In many cases, 8 12 16 24 32 should be a good choice.\n"
872 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
873 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
877 f = FindFont(Cmd_Argv(1), true);
880 Con_Printf("font function not found\n");
885 filelist = "gfx/conchars";
887 filelist = Cmd_Argv(2);
889 memset(f->fallbacks, 0, sizeof(f->fallbacks));
890 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
892 // first font is handled "normally"
893 c = strchr(filelist, ':');
894 cm = strchr(filelist, ',');
895 if(c && (!cm || c < cm))
896 f->req_face = atoi(c+1);
903 if(!c || (c - filelist) > MAX_QPATH)
904 strlcpy(mainfont, filelist, sizeof(mainfont));
907 memcpy(mainfont, filelist, c - filelist);
908 mainfont[c - filelist] = 0;
911 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
913 c = strchr(filelist, ',');
919 c = strchr(filelist, ':');
920 cm = strchr(filelist, ',');
921 if(c && (!cm || c < cm))
922 f->fallback_faces[i] = atoi(c+1);
925 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
928 if(!c || (c-filelist) > MAX_QPATH)
930 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
934 memcpy(f->fallbacks[i], filelist, c - filelist);
935 f->fallbacks[i][c - filelist] = 0;
939 // for now: by default load only one size: the default size
941 for(i = 1; i < MAX_FONT_SIZES; ++i)
942 f->req_sizes[i] = -1;
948 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
951 if (!strcmp(Cmd_Argv(i), "scale"))
955 scale = atof(Cmd_Argv(i));
958 if (!strcmp(Cmd_Argv(i), "voffset"))
962 voffset = atof(Cmd_Argv(i));
967 continue; // no slot for other sizes
969 // parse one of sizes
970 sz = atof(Cmd_Argv(i));
971 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
973 // search for duplicated sizes
975 for (j=0; j<sizes; j++)
976 if (f->req_sizes[j] == sz)
979 continue; // sz already in req_sizes, don't add it again
981 if (sizes == MAX_FONT_SIZES)
983 Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
987 f->req_sizes[sizes] = sz;
993 LoadFont(true, mainfont, f, scale, voffset);
1001 static void gl_draw_start(void)
1005 drawtexturepool = R_AllocTexturePool();
1008 memset(cachepichash, 0, sizeof(cachepichash));
1012 // load default font textures
1013 for(i = 0; i < dp_fonts.maxsize; ++i)
1014 if (dp_fonts.f[i].title[0])
1015 LoadFont(false, va(vabuf, sizeof(vabuf), "gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
1017 // draw the loading screen so people have something to see in the newly opened window
1018 SCR_UpdateLoadingScreen(true);
1021 static void gl_draw_shutdown(void)
1025 R_FreeTexturePool(&drawtexturepool);
1028 memset(cachepichash, 0, sizeof(cachepichash));
1031 static void gl_draw_newmap(void)
1036 void GL_Draw_Init (void)
1040 Cvar_RegisterVariable(&r_font_postprocess_blur);
1041 Cvar_RegisterVariable(&r_font_postprocess_outline);
1042 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
1043 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
1044 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
1045 Cvar_RegisterVariable(&r_font_hinting);
1046 Cvar_RegisterVariable(&r_font_antialias);
1047 Cvar_RegisterVariable(&r_textshadow);
1048 Cvar_RegisterVariable(&r_textbrightness);
1049 Cvar_RegisterVariable(&r_textcontrast);
1051 // allocate fonts storage
1052 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
1053 dp_fonts.maxsize = MAX_FONTS;
1054 dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
1055 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
1057 // assign starting font names
1058 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
1059 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
1060 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
1061 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
1062 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
1063 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
1064 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
1065 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
1066 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
1067 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
1068 if(!FONT_USER(i)->title[0])
1069 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
1071 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
1072 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
1075 static void _DrawQ_Setup(void) // see R_ResetViewRendering2D
1077 if (r_refdef.draw2dstage == 1)
1079 r_refdef.draw2dstage = 1;
1081 R_ResetViewRendering2D_Common(0, NULL, NULL, vid_conwidth.integer, vid_conheight.integer);
1084 qboolean r_draw2d_force = false;
1085 static void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1088 if(!r_draw2d.integer && !r_draw2d_force)
1090 DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1092 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1094 if(flags == DRAWFLAG_ADDITIVE)
1096 GL_DepthMask(false);
1097 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1099 else if(flags == DRAWFLAG_MODULATE)
1101 GL_DepthMask(false);
1102 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1104 else if(flags == DRAWFLAG_2XMODULATE)
1106 GL_DepthMask(false);
1107 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1109 else if(flags == DRAWFLAG_SCREEN)
1111 GL_DepthMask(false);
1112 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1116 GL_DepthMask(false);
1117 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1122 GL_BlendFunc(GL_ONE, GL_ZERO);
1126 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1130 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1131 if(!r_draw2d.integer && !r_draw2d_force)
1134 // R_Mesh_ResetTextureState();
1135 floats[12] = 0.0f;floats[13] = 0.0f;
1136 floats[14] = 1.0f;floats[15] = 0.0f;
1137 floats[16] = 1.0f;floats[17] = 1.0f;
1138 floats[18] = 0.0f;floats[19] = 1.0f;
1139 floats[20] = floats[24] = floats[28] = floats[32] = red;
1140 floats[21] = floats[25] = floats[29] = floats[33] = green;
1141 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1142 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1148 height = pic->height;
1149 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1152 // AK07: lets be texel correct on the corners
1154 float horz_offset = 0.5f / pic->width;
1155 float vert_offset = 0.5f / pic->height;
1157 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1158 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1159 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1160 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1165 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1167 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1168 floats[0] = floats[9] = x;
1169 floats[1] = floats[4] = y;
1170 floats[3] = floats[6] = x + width;
1171 floats[7] = floats[10] = y + height;
1173 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1174 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1177 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)
1180 float af = DEG2RAD(-angle); // forward
1181 float ar = DEG2RAD(-angle + 90); // right
1182 float sinaf = sin(af);
1183 float cosaf = cos(af);
1184 float sinar = sin(ar);
1185 float cosar = cos(ar);
1187 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1188 if(!r_draw2d.integer && !r_draw2d_force)
1191 // R_Mesh_ResetTextureState();
1197 height = pic->height;
1198 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1201 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1203 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1206 floats[0] = x - cosaf*org_x - cosar*org_y;
1207 floats[1] = y - sinaf*org_x - sinar*org_y;
1210 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1211 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1214 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1215 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1218 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1219 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1221 floats[12] = 0.0f;floats[13] = 0.0f;
1222 floats[14] = 1.0f;floats[15] = 0.0f;
1223 floats[16] = 1.0f;floats[17] = 1.0f;
1224 floats[18] = 0.0f;floats[19] = 1.0f;
1225 floats[20] = floats[24] = floats[28] = floats[32] = red;
1226 floats[21] = floats[25] = floats[29] = floats[33] = green;
1227 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1228 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1230 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1231 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1234 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1238 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1239 if(!r_draw2d.integer && !r_draw2d_force)
1242 // R_Mesh_ResetTextureState();
1243 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1245 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1246 floats[0] = floats[9] = x;
1247 floats[1] = floats[4] = y;
1248 floats[3] = floats[6] = x + width;
1249 floats[7] = floats[10] = y + height;
1250 floats[12] = 0.0f;floats[13] = 0.0f;
1251 floats[14] = 1.0f;floats[15] = 0.0f;
1252 floats[16] = 1.0f;floats[17] = 1.0f;
1253 floats[18] = 0.0f;floats[19] = 1.0f;
1254 floats[20] = floats[24] = floats[28] = floats[32] = red;
1255 floats[21] = floats[25] = floats[29] = floats[33] = green;
1256 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1257 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1259 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1260 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1263 /// color tag printing
1264 static const vec4_t string_colors[] =
1267 // LordHavoc: why on earth is cyan before magenta in Quake3?
1268 // LordHavoc: note: Doom3 uses white for [0] and [7]
1269 {0.0, 0.0, 0.0, 1.0}, // black
1270 {1.0, 0.0, 0.0, 1.0}, // red
1271 {0.0, 1.0, 0.0, 1.0}, // green
1272 {1.0, 1.0, 0.0, 1.0}, // yellow
1273 {0.0, 0.0, 1.0, 1.0}, // blue
1274 {0.0, 1.0, 1.0, 1.0}, // cyan
1275 {1.0, 0.0, 1.0, 1.0}, // magenta
1276 {1.0, 1.0, 1.0, 1.0}, // white
1277 // [515]'s BX_COLOREDTEXT extension
1278 {1.0, 1.0, 1.0, 0.5}, // half transparent
1279 {0.5, 0.5, 0.5, 1.0} // half brightness
1280 // Black's color table
1281 //{1.0, 1.0, 1.0, 1.0},
1282 //{1.0, 0.0, 0.0, 1.0},
1283 //{0.0, 1.0, 0.0, 1.0},
1284 //{0.0, 0.0, 1.0, 1.0},
1285 //{1.0, 1.0, 0.0, 1.0},
1286 //{0.0, 1.0, 1.0, 1.0},
1287 //{1.0, 0.0, 1.0, 1.0},
1288 //{0.1, 0.1, 0.1, 1.0}
1291 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1293 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1295 float C = r_textcontrast.value;
1296 float B = r_textbrightness.value;
1297 if (colorindex & 0x10000) // that bit means RGB color
1299 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1300 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1301 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1302 color[3] = (colorindex & 0xf) / 15.0;
1305 Vector4Copy(string_colors[colorindex], color);
1306 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1309 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1310 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1314 // NOTE: this function always draws exactly one character if maxwidth <= 0
1315 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)
1317 const char *text_start = text;
1318 int colorindex = STRING_COLOR_DEFAULT;
1321 Uchar ch, mapch, nextch;
1322 Uchar prevch = 0; // used for kerning
1327 ft2_font_map_t *fontmap = NULL;
1328 ft2_font_map_t *map = NULL;
1329 //ft2_font_map_t *prevmap = NULL;
1330 ft2_font_t *ft2 = fnt->ft2;
1332 qboolean snap = true;
1333 qboolean least_one = false;
1334 float dw; // display w
1335 //float dh; // display h
1336 const float *width_of;
1343 // do this in the end
1344 w *= fnt->settings.scale;
1345 h *= fnt->settings.scale;
1347 // find the most fitting size:
1351 map_index = Font_IndexForSize(ft2, h, &w, &h);
1353 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1354 fontmap = Font_MapForIndex(ft2, map_index);
1363 if (!outcolor || *outcolor == -1)
1364 colorindex = STRING_COLOR_DEFAULT;
1366 colorindex = *outcolor;
1368 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1369 // ftbase_x = snap_to_pixel_x(0);
1374 maxwidth = -maxwidth;
1378 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1381 width_of = fontmap->width_of;
1383 width_of = fnt->width_of;
1385 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1388 nextch = ch = u8_getnchar(text, &text, bytes_left);
1389 i = text - text_start;
1392 if (ch == ' ' && !fontmap)
1394 if(!least_one || i0) // never skip the first character
1395 if(x + width_of[(int) ' '] * dw > maxwidth)
1398 break; // oops, can't draw this
1400 x += width_of[(int) ' '] * dw;
1403 // i points to the char after ^
1404 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1406 ch = *text; // colors are ascii, so no u8_ needed
1407 if (ch <= '9' && ch >= '0') // ^[0-9] found
1409 colorindex = ch - '0';
1414 // i points to the char after ^...
1415 // i+3 points to 3 in ^x123
1416 // i+3 == *maxlen would mean that char is missing
1417 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1419 // building colorindex...
1420 ch = tolower(text[1]);
1421 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1422 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1423 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1424 else tempcolorindex = 0;
1427 ch = tolower(text[2]);
1428 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1429 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1430 else tempcolorindex = 0;
1433 ch = tolower(text[3]);
1434 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1435 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1436 else tempcolorindex = 0;
1439 colorindex = tempcolorindex | 0xf;
1440 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1448 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1457 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1464 map = ft2_oldstyle_map;
1466 if(!least_one || i0) // never skip the first character
1467 if(x + width_of[ch] * dw > maxwidth)
1470 break; // oops, can't draw this
1472 x += width_of[ch] * dw;
1474 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1476 map = FontMap_FindForChar(fontmap, ch);
1479 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1485 mapch = ch - map->start;
1486 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1488 x += map->glyphs[mapch].advance_x * dw;
1497 *outcolor = colorindex;
1502 float DrawQ_Color[4];
1503 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)
1505 int shadow, colorindex = STRING_COLOR_DEFAULT;
1507 float x = startx, y, s, t, u, v, thisw;
1508 float *av, *at, *ac;
1510 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1511 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1512 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1513 Uchar ch, mapch, nextch;
1514 Uchar prevch = 0; // used for kerning
1517 //ft2_font_map_t *prevmap = NULL; // the previous map
1518 ft2_font_map_t *map = NULL; // the currently used map
1519 ft2_font_map_t *fontmap = NULL; // the font map for the size
1521 const char *text_start = text;
1523 ft2_font_t *ft2 = fnt->ft2;
1524 qboolean snap = true;
1528 const float *width_of;
1531 tw = R_TextureWidth(fnt->tex);
1532 th = R_TextureHeight(fnt->tex);
1540 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1541 w *= fnt->settings.scale;
1542 h *= fnt->settings.scale;
1547 map_index = Font_IndexForSize(ft2, h, &w, &h);
1549 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1550 fontmap = Font_MapForIndex(ft2, map_index);
1556 // draw the font at its baseline when using freetype
1558 ftbase_y = dh * (4.5/6.0);
1563 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1564 if(!r_draw2d.integer && !r_draw2d_force)
1565 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1567 // R_Mesh_ResetTextureState();
1569 R_Mesh_TexBind(0, fnt->tex);
1570 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1577 //ftbase_x = snap_to_pixel_x(ftbase_x);
1580 startx = snap_to_pixel_x(startx, 0.4);
1581 starty = snap_to_pixel_y(starty, 0.4);
1582 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1585 pix_x = vid.width / vid_conwidth.value;
1586 pix_y = vid.height / vid_conheight.value;
1589 width_of = fontmap->width_of;
1591 width_of = fnt->width_of;
1593 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1598 if (!outcolor || *outcolor == -1)
1599 colorindex = STRING_COLOR_DEFAULT;
1601 colorindex = *outcolor;
1603 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1610 x += r_textshadow.value * vid.width / vid_conwidth.value;
1611 y += r_textshadow.value * vid.height / vid_conheight.value;
1614 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1616 nextch = ch = u8_getnchar(text, &text, bytes_left);
1617 i = text - text_start;
1620 if (ch == ' ' && !fontmap)
1622 x += width_of[(int) ' '] * dw;
1625 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1627 ch = *text; // colors are ascii, so no u8_ needed
1628 if (ch <= '9' && ch >= '0') // ^[0-9] found
1630 colorindex = ch - '0';
1631 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1636 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1638 // building colorindex...
1639 ch = tolower(text[1]);
1640 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1641 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1642 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1643 else tempcolorindex = 0;
1646 ch = tolower(text[2]);
1647 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1648 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1649 else tempcolorindex = 0;
1652 ch = tolower(text[3]);
1653 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1654 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1655 else tempcolorindex = 0;
1658 colorindex = tempcolorindex | 0xf;
1659 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1660 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1661 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1669 else if (ch == STRING_COLOR_TAG)
1678 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1679 // this way we don't need to rebind fnt->tex for every old-style character
1680 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1683 x += 1.0/pix_x * r_textshadow.value;
1684 y += 1.0/pix_y * r_textshadow.value;
1686 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1694 if (map != ft2_oldstyle_map)
1698 // switching from freetype to non-freetype rendering
1699 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1700 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1706 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1707 map = ft2_oldstyle_map;
1711 //num = (unsigned char) text[i];
1712 //thisw = fnt->width_of[num];
1713 thisw = fnt->width_of[ch];
1714 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1715 s = (ch & 15)*0.0625f + (0.5f / tw);
1716 t = (ch >> 4)*0.0625f + (0.5f / th);
1717 u = 0.0625f * thisw - (1.0f / tw);
1718 v = 0.0625f - (1.0f / th);
1719 ac[ 0] = DrawQ_Color[0];ac[ 1] = DrawQ_Color[1];ac[ 2] = DrawQ_Color[2];ac[ 3] = DrawQ_Color[3];
1720 ac[ 4] = DrawQ_Color[0];ac[ 5] = DrawQ_Color[1];ac[ 6] = DrawQ_Color[2];ac[ 7] = DrawQ_Color[3];
1721 ac[ 8] = DrawQ_Color[0];ac[ 9] = DrawQ_Color[1];ac[10] = DrawQ_Color[2];ac[11] = DrawQ_Color[3];
1722 ac[12] = DrawQ_Color[0];ac[13] = DrawQ_Color[1];ac[14] = DrawQ_Color[2];ac[15] = DrawQ_Color[3];
1723 at[ 0] = s ; at[ 1] = t ;
1724 at[ 2] = s+u ; at[ 3] = t ;
1725 at[ 4] = s+u ; at[ 5] = t+v ;
1726 at[ 6] = s ; at[ 7] = t+v ;
1727 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1728 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1729 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1730 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1735 if (batchcount >= QUADELEMENTS_MAXQUADS)
1737 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1738 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1744 x += width_of[ch] * dw;
1746 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1748 // new charmap - need to render
1751 // we need a different character map, render what we currently have:
1752 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1753 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1760 map = FontMap_FindForChar(fontmap, ch);
1763 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1770 // this shouldn't happen
1775 R_SetupShader_Generic(map->pic->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1778 mapch = ch - map->start;
1779 thisw = map->glyphs[mapch].advance_x;
1783 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1790 ac[ 0] = DrawQ_Color[0]; ac[ 1] = DrawQ_Color[1]; ac[ 2] = DrawQ_Color[2]; ac[ 3] = DrawQ_Color[3];
1791 ac[ 4] = DrawQ_Color[0]; ac[ 5] = DrawQ_Color[1]; ac[ 6] = DrawQ_Color[2]; ac[ 7] = DrawQ_Color[3];
1792 ac[ 8] = DrawQ_Color[0]; ac[ 9] = DrawQ_Color[1]; ac[10] = DrawQ_Color[2]; ac[11] = DrawQ_Color[3];
1793 ac[12] = DrawQ_Color[0]; ac[13] = DrawQ_Color[1]; ac[14] = DrawQ_Color[2]; ac[15] = DrawQ_Color[3];
1794 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1795 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1796 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1797 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1798 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1799 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1800 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1801 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1810 if (batchcount >= QUADELEMENTS_MAXQUADS)
1812 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1813 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1826 x -= 1.0/pix_x * r_textshadow.value;
1827 y -= 1.0/pix_y * r_textshadow.value;
1833 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1834 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1838 *outcolor = colorindex;
1840 // note: this relies on the proper text (not shadow) being drawn last
1844 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)
1846 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1849 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)
1851 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1854 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1856 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1859 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1861 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1866 // no ^xrgb management
1867 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1869 int color, numchars = 0;
1870 char *outputend2c = output2c + maxoutchars - 2;
1871 if (!outcolor || *outcolor == -1)
1872 color = STRING_COLOR_DEFAULT;
1876 maxreadchars = 1<<30;
1877 textend = text + maxreadchars;
1878 while (text != textend && *text)
1880 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1882 if (text[1] == STRING_COLOR_TAG)
1884 else if (text[1] >= '0' && text[1] <= '9')
1886 color = text[1] - '0';
1891 if (output2c >= outputend2c)
1893 *output2c++ = *text++;
1894 *output2c++ = color;
1897 output2c[0] = output2c[1] = 0;
1904 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)
1908 _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1909 if(!r_draw2d.integer && !r_draw2d_force)
1912 // R_Mesh_ResetTextureState();
1918 height = pic->height;
1919 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1922 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1924 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1925 floats[0] = floats[9] = x;
1926 floats[1] = floats[4] = y;
1927 floats[3] = floats[6] = x + width;
1928 floats[7] = floats[10] = y + height;
1929 floats[12] = s1;floats[13] = t1;
1930 floats[14] = s2;floats[15] = t2;
1931 floats[16] = s4;floats[17] = t4;
1932 floats[18] = s3;floats[19] = t3;
1933 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1934 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1935 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1936 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1938 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1939 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1942 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1945 if(!r_draw2d.integer && !r_draw2d_force)
1947 DrawQ_ProcessDrawFlag(flags, hasalpha);
1949 // R_Mesh_ResetTextureState();
1950 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1952 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1953 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1956 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1960 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1961 if(!r_draw2d.integer && !r_draw2d_force)
1965 switch(vid.renderpath)
1967 case RENDERPATH_GL11:
1968 case RENDERPATH_GL13:
1969 case RENDERPATH_GL20:
1972 qglBegin(GL_LINE_LOOP);
1973 for (num = 0;num < mesh->num_vertices;num++)
1975 if (mesh->data_color4f)
1976 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]);
1977 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1983 case RENDERPATH_D3D9:
1984 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1986 case RENDERPATH_D3D10:
1987 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1989 case RENDERPATH_D3D11:
1990 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1992 case RENDERPATH_SOFT:
1993 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1995 case RENDERPATH_GLES1:
1996 case RENDERPATH_GLES2:
1997 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2002 //[515]: this is old, delete
2003 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
2005 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
2006 if(!r_draw2d.integer && !r_draw2d_force)
2009 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
2011 switch(vid.renderpath)
2013 case RENDERPATH_GL11:
2014 case RENDERPATH_GL13:
2015 case RENDERPATH_GL20:
2019 //qglLineWidth(width);CHECKGLERROR
2021 GL_Color(r,g,b,alpha);
2024 qglVertex2f(x1, y1);
2025 qglVertex2f(x2, y2);
2030 case RENDERPATH_D3D9:
2031 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2033 case RENDERPATH_D3D10:
2034 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2036 case RENDERPATH_D3D11:
2037 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2039 case RENDERPATH_SOFT:
2040 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2042 case RENDERPATH_GLES1:
2043 case RENDERPATH_GLES2:
2044 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2049 void DrawQ_Lines (float width, int numlines, const float *vertex3f, const float *color4f, int flags)
2052 qboolean hasalpha = false;
2053 for (i = 0;i < numlines*2;i++)
2054 if (color4f[i*4+3] < 1.0f)
2057 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, hasalpha ? 0.5f : 1.0f);
2059 if(!r_draw2d.integer && !r_draw2d_force)
2062 switch(vid.renderpath)
2064 case RENDERPATH_GL11:
2065 case RENDERPATH_GL13:
2066 case RENDERPATH_GL20:
2069 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
2071 //qglLineWidth(width);CHECKGLERROR
2074 R_Mesh_PrepareVertices_Generic_Arrays(numlines*2, vertex3f, color4f, NULL);
2075 qglDrawArrays(GL_LINES, 0, numlines*2);
2078 case RENDERPATH_D3D9:
2079 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2081 case RENDERPATH_D3D10:
2082 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2084 case RENDERPATH_D3D11:
2085 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2087 case RENDERPATH_SOFT:
2088 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2090 case RENDERPATH_GLES1:
2091 case RENDERPATH_GLES2:
2092 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2097 void DrawQ_SetClipArea(float x, float y, float width, float height)
2102 // We have to convert the con coords into real coords
2103 // OGL uses top to bottom
2104 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
2105 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
2106 iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
2107 ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
2108 switch(vid.renderpath)
2110 case RENDERPATH_GL11:
2111 case RENDERPATH_GL13:
2112 case RENDERPATH_GL20:
2113 case RENDERPATH_GLES1:
2114 case RENDERPATH_GLES2:
2115 case RENDERPATH_SOFT:
2116 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
2118 case RENDERPATH_D3D9:
2119 GL_Scissor(ix, iy, iw, ih);
2121 case RENDERPATH_D3D10:
2122 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2124 case RENDERPATH_D3D11:
2125 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2129 GL_ScissorTest(true);
2132 void DrawQ_ResetClipArea(void)
2135 GL_ScissorTest(false);
2138 void DrawQ_Finish(void)
2140 r_refdef.draw2dstage = 0;
2143 void DrawQ_RecalcView(void)
2145 if(r_refdef.draw2dstage)
2146 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2149 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2150 void R_DrawGamma(void)
2153 switch(vid.renderpath)
2155 case RENDERPATH_GL20:
2156 case RENDERPATH_D3D9:
2157 case RENDERPATH_D3D10:
2158 case RENDERPATH_D3D11:
2159 case RENDERPATH_GLES2:
2160 if (vid_usinghwgamma || v_glslgamma.integer)
2163 case RENDERPATH_GL11:
2164 case RENDERPATH_GL13:
2165 if (vid_usinghwgamma)
2168 case RENDERPATH_GLES1:
2169 case RENDERPATH_SOFT:
2172 // all the blends ignore depth
2173 // R_Mesh_ResetTextureState();
2174 R_SetupShader_Generic_NoTexture(true, true);
2176 GL_DepthRange(0, 1);
2177 GL_PolygonOffset(0, 0);
2178 GL_DepthTest(false);
2180 // interpretation of brightness and contrast:
2181 // color range := brightness .. (brightness + contrast)
2182 // i.e. "c *= contrast; c += brightness"
2183 // plausible values for brightness thus range from -contrast to 1
2185 // apply pre-brightness (subtractive brightness, for where contrast was >= 1)
2186 if (vid.support.ext_blend_subtract)
2188 if (v_color_enable.integer)
2190 c[0] = -v_color_black_r.value / v_color_white_r.value;
2191 c[1] = -v_color_black_g.value / v_color_white_g.value;
2192 c[2] = -v_color_black_b.value / v_color_white_b.value;
2195 c[0] = c[1] = c[2] = -v_brightness.value / v_contrast.value;
2196 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2198 // need SUBTRACTIVE blending to do this!
2199 GL_BlendEquationSubtract(true);
2200 GL_BlendFunc(GL_ONE, GL_ONE);
2201 GL_Color(c[0], c[1], c[2], 1);
2202 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2203 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2204 GL_BlendEquationSubtract(false);
2209 if (v_color_enable.integer)
2211 c[0] = v_color_white_r.value;
2212 c[1] = v_color_white_g.value;
2213 c[2] = v_color_white_b.value;
2216 c[0] = c[1] = c[2] = v_contrast.value;
2217 if (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2219 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2220 while (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2223 cc[0] = bound(0, c[0] - 1, 1);
2224 cc[1] = bound(0, c[1] - 1, 1);
2225 cc[2] = bound(0, c[2] - 1, 1);
2226 GL_Color(cc[0], cc[1], cc[2], 1);
2227 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2228 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2234 if (c[0] <= 0.997f || c[1] <= 0.997f || c[2] <= 0.997f)
2236 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
2237 GL_Color(c[0], c[1], c[2], 1);
2238 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2239 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2242 // apply post-brightness (additive brightness, for where contrast was <= 1)
2243 if (v_color_enable.integer)
2245 c[0] = v_color_black_r.value;
2246 c[1] = v_color_black_g.value;
2247 c[2] = v_color_black_b.value;
2250 c[0] = c[1] = c[2] = v_brightness.value;
2251 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2253 GL_BlendFunc(GL_ONE, GL_ONE);
2254 GL_Color(c[0], c[1], c[2], 1);
2255 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2256 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);