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 | TEXF_MIPMAP))) // ignore TEXF_COMPRESS when comparing, because fallback pics remove the flag, and ignore TEXF_MIPMAP because QC specifies that
348 if(!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
351 pic->autoload = false; // persist it
353 goto reload; // load it below, and then persist
360 if (numcachepics == MAX_CACHED_PICS)
362 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
363 // FIXME: support NULL in callers?
364 return cachepics; // return the first one
366 pic = cachepics + (numcachepics++);
367 memset(pic, 0, sizeof(*pic));
368 strlcpy (pic->name, path, sizeof(pic->name));
370 pic->chain = cachepichash[hashkey];
371 cachepichash[hashkey] = pic;
374 // TODO why does this crash?
375 if(pic->allow_free_tex && pic->tex)
376 R_PurgeTexture(pic->tex);
378 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
379 pic->flags = cachepicflags;
380 pic->tex = CL_GetDynTexture( path );
381 // if so, set the width/height, too
383 pic->allow_free_tex = false;
384 pic->width = R_TextureWidth(pic->tex);
385 pic->height = R_TextureHeight(pic->tex);
386 // we're done now (early-out)
390 pic->allow_free_tex = true;
392 pic->hasalpha = true; // assume alpha unless we know it has none
393 pic->texflags = texflags;
394 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
395 pic->lastusedframe = draw_frame;
397 // load a high quality image from disk if possible
398 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)))
400 // note this loads even if autoload is true, otherwise we can't get the width/height
402 pic->hasalpha = ddshasalpha;
403 pic->width = R_TextureWidth(pic->tex);
404 pic->height = R_TextureHeight(pic->tex);
406 if (!loaded && ((pixels = loadimagepixelsbgra(pic->name, false, true, false, NULL)) || (!strncmp(pic->name, "gfx/", 4) && (pixels = loadimagepixelsbgra(pic->name+4, false, true, false, NULL)))))
409 pic->hasalpha = false;
410 if (pic->texflags & TEXF_ALPHA)
412 for (j = 3;j < image_width * image_height * 4;j += 4)
416 pic->hasalpha = true;
422 pic->width = image_width;
423 pic->height = image_height;
426 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);
428 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
429 R_SaveTextureDDSFile(pic->tex, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
435 pic->autoload = false;
436 // never compress the fallback images
437 pic->texflags &= ~TEXF_COMPRESS;
440 // now read the low quality version (wad or lmp file), and take the pic
441 // size from that even if we don't upload the texture, this way the pics
442 // show up the right size in the menu even if they were replaced with
443 // higher or lower resolution versions
444 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", pic->name);
445 if (!strncmp(pic->name, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
447 if (developer_loading.integer)
448 Con_Printf("loading lump \"%s\"\n", pic->name);
452 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
453 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
454 // if no high quality replacement image was found, upload the original low quality texture
458 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);
463 else if ((lmpdata = W_GetLumpName (pic->name + 4)))
465 if (developer_loading.integer)
466 Con_Printf("loading gfx.wad lump \"%s\"\n", pic->name + 4);
468 if (!strcmp(pic->name, "gfx/conchars"))
470 // conchars is a raw image and with color 0 as transparent instead of 255
473 // if no high quality replacement image was found, upload the original low quality texture
477 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, 128, 128, lmpdata, vid.sRGB2D != 0 ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
482 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
483 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
484 // if no high quality replacement image was found, upload the original low quality texture
488 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);
500 // if it's not found on disk, generate an image
501 pic->tex = draw_generatepic(pic->name, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
502 pic->width = R_TextureWidth(pic->tex);
503 pic->height = R_TextureHeight(pic->tex);
504 pic->allow_free_tex = (pic->tex != r_texture_notexture);
510 cachepic_t *Draw_CachePic (const char *path)
512 return Draw_CachePic_Flags (path, 0); // default to persistent!
515 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
518 if (pic->autoload && !pic->tex)
520 if (pic->tex == NULL && r_texture_dds_load.integer != 0)
522 qboolean ddshasalpha;
523 float ddsavgcolor[4];
524 pic->tex = R_LoadTextureDDSFile(drawtexturepool, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), vid.sRGB2D, pic->texflags, &ddshasalpha, ddsavgcolor, 0);
526 if (pic->tex == NULL)
528 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, vid.sRGB2D);
530 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
531 R_SaveTextureDDSFile(pic->tex, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
534 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
536 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, vid.sRGB2D);
538 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
539 R_SaveTextureDDSFile(pic->tex, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
542 if (pic->tex == NULL)
543 pic->tex = draw_generatepic(pic->name, true);
545 pic->lastusedframe = draw_frame;
549 void Draw_Frame(void)
553 static double nextpurgetime;
554 if (nextpurgetime > realtime)
556 nextpurgetime = realtime + 0.05;
557 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
559 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
561 R_FreeTexture(pic->tex);
568 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
573 crc = CRC_Block((unsigned char *)picname, strlen(picname));
574 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
575 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
576 if (!strcmp (picname, pic->name))
581 if (pic->flags == CACHEPICFLAG_NEWPIC && pic->tex && pic->width == width && pic->height == height)
583 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, 0, width, height, 1);
589 if (numcachepics == MAX_CACHED_PICS)
591 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
592 // FIXME: support NULL in callers?
593 return cachepics; // return the first one
595 pic = cachepics + (numcachepics++);
596 memset(pic, 0, sizeof(*pic));
597 strlcpy (pic->name, picname, sizeof(pic->name));
599 pic->chain = cachepichash[hashkey];
600 cachepichash[hashkey] = pic;
603 pic->flags = CACHEPICFLAG_NEWPIC; // disable texflags checks in Draw_CachePic
605 pic->height = height;
606 if (pic->allow_free_tex && pic->tex)
607 R_FreeTexture(pic->tex);
608 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0), -1, NULL);
612 void Draw_FreePic(const char *picname)
617 // this doesn't really free the pic, but does free it's texture
618 crc = CRC_Block((unsigned char *)picname, strlen(picname));
619 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
620 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
622 if (!strcmp (picname, pic->name) && pic->tex)
624 R_FreeTexture(pic->tex);
633 static float snap_to_pixel_x(float x, float roundUpAt);
634 extern int con_linewidth; // to force rewrapping
635 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
639 char widthfile[MAX_QPATH];
641 fs_offset_t widthbufsize;
643 if(override || !fnt->texpath[0])
645 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
646 // load the cvars when the font is FIRST loader
647 fnt->settings.scale = scale;
648 fnt->settings.voffset = voffset;
649 fnt->settings.antialias = r_font_antialias.integer;
650 fnt->settings.hinting = r_font_hinting.integer;
651 fnt->settings.outline = r_font_postprocess_outline.value;
652 fnt->settings.blur = r_font_postprocess_blur.value;
653 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
654 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
655 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
658 if (fnt->settings.scale <= 0)
659 fnt->settings.scale = 1;
661 if(drawtexturepool == NULL)
662 return; // before gl_draw_start, so will be loaded later
666 // clear freetype font
667 Font_UnloadFont(fnt->ft2);
672 if(fnt->req_face != -1)
674 if(!Font_LoadFont(fnt->texpath, fnt))
675 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
678 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
679 if(fnt->tex == r_texture_notexture)
681 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
683 if (!fnt->fallbacks[i][0])
685 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
686 if(fnt->tex != r_texture_notexture)
689 if(fnt->tex == r_texture_notexture)
691 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
692 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
695 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
698 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
700 // unspecified width == 1 (base width)
701 for(ch = 0; ch < 256; ++ch)
702 fnt->width_of[ch] = 1;
704 // FIXME load "name.width", if it fails, fill all with 1
705 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
707 float extraspacing = 0;
708 const char *p = widthbuf;
713 if(!COM_ParseToken_Simple(&p, false, false, true))
731 fnt->width_of[ch] = atof(com_token) + extraspacing;
735 if(!strcmp(com_token, "extraspacing"))
737 if(!COM_ParseToken_Simple(&p, false, false, true))
739 extraspacing = atof(com_token);
741 else if(!strcmp(com_token, "scale"))
743 if(!COM_ParseToken_Simple(&p, false, false, true))
745 fnt->settings.scale = atof(com_token);
749 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
750 if(!COM_ParseToken_Simple(&p, false, false, true))
762 for (i = 0; i < MAX_FONT_SIZES; ++i)
764 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
767 for(ch = 0; ch < 256; ++ch)
768 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
772 maxwidth = fnt->width_of[0];
773 for(i = 1; i < 256; ++i)
774 maxwidth = max(maxwidth, fnt->width_of[i]);
775 fnt->maxwidth = maxwidth;
777 // fix up maxwidth for overlap
778 fnt->maxwidth *= fnt->settings.scale;
780 if(fnt == FONT_CONSOLE)
781 con_linewidth = -1; // rewrap console in next frame
784 extern cvar_t developer_font;
785 dp_font_t *FindFont(const char *title, qboolean allocate_new)
790 for(i = 0; i < dp_fonts.maxsize; ++i)
791 if(!strcmp(dp_fonts.f[i].title, title))
792 return &dp_fonts.f[i];
793 // if not found - try allocate
796 // find any font with empty title
797 for(i = 0; i < dp_fonts.maxsize; ++i)
799 if(!strcmp(dp_fonts.f[i].title, ""))
801 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
802 return &dp_fonts.f[i];
805 // if no any 'free' fonts - expand buffer
806 oldsize = dp_fonts.maxsize;
807 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
808 if (developer_font.integer)
809 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", oldsize, dp_fonts.maxsize);
810 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
811 // relink ft2 structures
812 for(i = 0; i < oldsize; ++i)
813 if (dp_fonts.f[i].ft2)
814 dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings;
815 // register a font in first expanded slot
816 strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
817 return &dp_fonts.f[oldsize];
822 static float snap_to_pixel_x(float x, float roundUpAt)
824 float pixelpos = x * vid.width / vid_conwidth.value;
825 int snap = (int) pixelpos;
826 if (pixelpos - snap >= roundUpAt) ++snap;
827 return ((float)snap * vid_conwidth.value / vid.width);
829 x = (int)(x * vid.width / vid_conwidth.value);
830 x = (x * vid_conwidth.value / vid.width);
835 static float snap_to_pixel_y(float y, float roundUpAt)
837 float pixelpos = y * vid.height / vid_conheight.value;
838 int snap = (int) pixelpos;
839 if (pixelpos - snap > roundUpAt) ++snap;
840 return ((float)snap * vid_conheight.value / vid.height);
842 y = (int)(y * vid.height / vid_conheight.value);
843 y = (y * vid_conheight.value / vid.height);
848 static void LoadFont_f(void)
852 const char *filelist, *c, *cm;
853 float sz, scale, voffset;
854 char mainfont[MAX_QPATH];
858 Con_Printf("Available font commands:\n");
859 for(i = 0; i < dp_fonts.maxsize; ++i)
860 if (dp_fonts.f[i].title[0])
861 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
862 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
863 "can specify multiple fonts and faces\n"
864 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
865 "to load face 2 of the font gfx/vera-sans and use face 1\n"
866 "of gfx/fallback as fallback font.\n"
867 "You can also specify a list of font sizes to load, like this:\n"
868 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
869 "In many cases, 8 12 16 24 32 should be a good choice.\n"
871 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
872 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
876 f = FindFont(Cmd_Argv(1), true);
879 Con_Printf("font function not found\n");
884 filelist = "gfx/conchars";
886 filelist = Cmd_Argv(2);
888 memset(f->fallbacks, 0, sizeof(f->fallbacks));
889 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
891 // first font is handled "normally"
892 c = strchr(filelist, ':');
893 cm = strchr(filelist, ',');
894 if(c && (!cm || c < cm))
895 f->req_face = atoi(c+1);
902 if(!c || (c - filelist) > MAX_QPATH)
903 strlcpy(mainfont, filelist, sizeof(mainfont));
906 memcpy(mainfont, filelist, c - filelist);
907 mainfont[c - filelist] = 0;
910 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
912 c = strchr(filelist, ',');
918 c = strchr(filelist, ':');
919 cm = strchr(filelist, ',');
920 if(c && (!cm || c < cm))
921 f->fallback_faces[i] = atoi(c+1);
924 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
927 if(!c || (c-filelist) > MAX_QPATH)
929 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
933 memcpy(f->fallbacks[i], filelist, c - filelist);
934 f->fallbacks[i][c - filelist] = 0;
938 // for now: by default load only one size: the default size
940 for(i = 1; i < MAX_FONT_SIZES; ++i)
941 f->req_sizes[i] = -1;
947 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
950 if (!strcmp(Cmd_Argv(i), "scale"))
954 scale = atof(Cmd_Argv(i));
957 if (!strcmp(Cmd_Argv(i), "voffset"))
961 voffset = atof(Cmd_Argv(i));
966 continue; // no slot for other sizes
968 // parse one of sizes
969 sz = atof(Cmd_Argv(i));
970 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
972 // search for duplicated sizes
974 for (j=0; j<sizes; j++)
975 if (f->req_sizes[j] == sz)
978 continue; // sz already in req_sizes, don't add it again
980 if (sizes == MAX_FONT_SIZES)
982 Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
986 f->req_sizes[sizes] = sz;
992 LoadFont(true, mainfont, f, scale, voffset);
1000 static void gl_draw_start(void)
1004 drawtexturepool = R_AllocTexturePool();
1007 memset(cachepichash, 0, sizeof(cachepichash));
1011 // load default font textures
1012 for(i = 0; i < dp_fonts.maxsize; ++i)
1013 if (dp_fonts.f[i].title[0])
1014 LoadFont(false, va(vabuf, sizeof(vabuf), "gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
1016 // draw the loading screen so people have something to see in the newly opened window
1017 SCR_UpdateLoadingScreen(true);
1020 static void gl_draw_shutdown(void)
1024 R_FreeTexturePool(&drawtexturepool);
1027 memset(cachepichash, 0, sizeof(cachepichash));
1030 static void gl_draw_newmap(void)
1035 void GL_Draw_Init (void)
1039 Cvar_RegisterVariable(&r_font_postprocess_blur);
1040 Cvar_RegisterVariable(&r_font_postprocess_outline);
1041 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
1042 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
1043 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
1044 Cvar_RegisterVariable(&r_font_hinting);
1045 Cvar_RegisterVariable(&r_font_antialias);
1046 Cvar_RegisterVariable(&r_textshadow);
1047 Cvar_RegisterVariable(&r_textbrightness);
1048 Cvar_RegisterVariable(&r_textcontrast);
1050 // allocate fonts storage
1051 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
1052 dp_fonts.maxsize = MAX_FONTS;
1053 dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
1054 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
1056 // assign starting font names
1057 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
1058 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
1059 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
1060 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
1061 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
1062 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
1063 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
1064 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
1065 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
1066 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
1067 if(!FONT_USER(i)->title[0])
1068 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
1070 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
1071 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
1074 static void _DrawQ_Setup(void) // see R_ResetViewRendering2D
1076 if (r_refdef.draw2dstage == 1)
1078 r_refdef.draw2dstage = 1;
1080 R_ResetViewRendering2D_Common(0, NULL, NULL, vid_conwidth.integer, vid_conheight.integer);
1083 qboolean r_draw2d_force = false;
1084 static void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1087 if(!r_draw2d.integer && !r_draw2d_force)
1089 DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1091 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1093 if(flags == DRAWFLAG_ADDITIVE)
1095 GL_DepthMask(false);
1096 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1098 else if(flags == DRAWFLAG_MODULATE)
1100 GL_DepthMask(false);
1101 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1103 else if(flags == DRAWFLAG_2XMODULATE)
1105 GL_DepthMask(false);
1106 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1108 else if(flags == DRAWFLAG_SCREEN)
1110 GL_DepthMask(false);
1111 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1115 GL_DepthMask(false);
1116 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1121 GL_BlendFunc(GL_ONE, GL_ZERO);
1125 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1129 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1130 if(!r_draw2d.integer && !r_draw2d_force)
1133 // R_Mesh_ResetTextureState();
1134 floats[12] = 0.0f;floats[13] = 0.0f;
1135 floats[14] = 1.0f;floats[15] = 0.0f;
1136 floats[16] = 1.0f;floats[17] = 1.0f;
1137 floats[18] = 0.0f;floats[19] = 1.0f;
1138 floats[20] = floats[24] = floats[28] = floats[32] = red;
1139 floats[21] = floats[25] = floats[29] = floats[33] = green;
1140 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1141 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1147 height = pic->height;
1148 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1151 // AK07: lets be texel correct on the corners
1153 float horz_offset = 0.5f / pic->width;
1154 float vert_offset = 0.5f / pic->height;
1156 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1157 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1158 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1159 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1164 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1166 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1167 floats[0] = floats[9] = x;
1168 floats[1] = floats[4] = y;
1169 floats[3] = floats[6] = x + width;
1170 floats[7] = floats[10] = y + height;
1172 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1173 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1176 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)
1179 float af = DEG2RAD(-angle); // forward
1180 float ar = DEG2RAD(-angle + 90); // right
1181 float sinaf = sin(af);
1182 float cosaf = cos(af);
1183 float sinar = sin(ar);
1184 float cosar = cos(ar);
1186 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1187 if(!r_draw2d.integer && !r_draw2d_force)
1190 // R_Mesh_ResetTextureState();
1196 height = pic->height;
1197 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1200 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1202 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1205 floats[0] = x - cosaf*org_x - cosar*org_y;
1206 floats[1] = y - sinaf*org_x - sinar*org_y;
1209 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1210 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1213 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1214 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1217 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1218 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1220 floats[12] = 0.0f;floats[13] = 0.0f;
1221 floats[14] = 1.0f;floats[15] = 0.0f;
1222 floats[16] = 1.0f;floats[17] = 1.0f;
1223 floats[18] = 0.0f;floats[19] = 1.0f;
1224 floats[20] = floats[24] = floats[28] = floats[32] = red;
1225 floats[21] = floats[25] = floats[29] = floats[33] = green;
1226 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1227 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1229 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1230 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1233 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1237 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1238 if(!r_draw2d.integer && !r_draw2d_force)
1241 // R_Mesh_ResetTextureState();
1242 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1244 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1245 floats[0] = floats[9] = x;
1246 floats[1] = floats[4] = y;
1247 floats[3] = floats[6] = x + width;
1248 floats[7] = floats[10] = y + height;
1249 floats[12] = 0.0f;floats[13] = 0.0f;
1250 floats[14] = 1.0f;floats[15] = 0.0f;
1251 floats[16] = 1.0f;floats[17] = 1.0f;
1252 floats[18] = 0.0f;floats[19] = 1.0f;
1253 floats[20] = floats[24] = floats[28] = floats[32] = red;
1254 floats[21] = floats[25] = floats[29] = floats[33] = green;
1255 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1256 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1258 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1259 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1262 /// color tag printing
1263 static const vec4_t string_colors[] =
1266 // LordHavoc: why on earth is cyan before magenta in Quake3?
1267 // LordHavoc: note: Doom3 uses white for [0] and [7]
1268 {0.0, 0.0, 0.0, 1.0}, // black
1269 {1.0, 0.0, 0.0, 1.0}, // red
1270 {0.0, 1.0, 0.0, 1.0}, // green
1271 {1.0, 1.0, 0.0, 1.0}, // yellow
1272 {0.0, 0.0, 1.0, 1.0}, // blue
1273 {0.0, 1.0, 1.0, 1.0}, // cyan
1274 {1.0, 0.0, 1.0, 1.0}, // magenta
1275 {1.0, 1.0, 1.0, 1.0}, // white
1276 // [515]'s BX_COLOREDTEXT extension
1277 {1.0, 1.0, 1.0, 0.5}, // half transparent
1278 {0.5, 0.5, 0.5, 1.0} // half brightness
1279 // Black's color table
1280 //{1.0, 1.0, 1.0, 1.0},
1281 //{1.0, 0.0, 0.0, 1.0},
1282 //{0.0, 1.0, 0.0, 1.0},
1283 //{0.0, 0.0, 1.0, 1.0},
1284 //{1.0, 1.0, 0.0, 1.0},
1285 //{0.0, 1.0, 1.0, 1.0},
1286 //{1.0, 0.0, 1.0, 1.0},
1287 //{0.1, 0.1, 0.1, 1.0}
1290 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1292 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1294 float C = r_textcontrast.value;
1295 float B = r_textbrightness.value;
1296 if (colorindex & 0x10000) // that bit means RGB color
1298 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1299 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1300 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1301 color[3] = (colorindex & 0xf) / 15.0;
1304 Vector4Copy(string_colors[colorindex], color);
1305 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1308 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1309 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1313 // NOTE: this function always draws exactly one character if maxwidth <= 0
1314 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)
1316 const char *text_start = text;
1317 int colorindex = STRING_COLOR_DEFAULT;
1320 Uchar ch, mapch, nextch;
1321 Uchar prevch = 0; // used for kerning
1326 ft2_font_map_t *fontmap = NULL;
1327 ft2_font_map_t *map = NULL;
1328 //ft2_font_map_t *prevmap = NULL;
1329 ft2_font_t *ft2 = fnt->ft2;
1331 qboolean snap = true;
1332 qboolean least_one = false;
1333 float dw; // display w
1334 //float dh; // display h
1335 const float *width_of;
1342 // do this in the end
1343 w *= fnt->settings.scale;
1344 h *= fnt->settings.scale;
1346 // find the most fitting size:
1350 map_index = Font_IndexForSize(ft2, h, &w, &h);
1352 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1353 fontmap = Font_MapForIndex(ft2, map_index);
1362 if (!outcolor || *outcolor == -1)
1363 colorindex = STRING_COLOR_DEFAULT;
1365 colorindex = *outcolor;
1367 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1368 // ftbase_x = snap_to_pixel_x(0);
1373 maxwidth = -maxwidth;
1377 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1380 width_of = fontmap->width_of;
1382 width_of = fnt->width_of;
1384 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1387 nextch = ch = u8_getnchar(text, &text, bytes_left);
1388 i = text - text_start;
1391 if (ch == ' ' && !fontmap)
1393 if(!least_one || i0) // never skip the first character
1394 if(x + width_of[(int) ' '] * dw > maxwidth)
1397 break; // oops, can't draw this
1399 x += width_of[(int) ' '] * dw;
1402 // i points to the char after ^
1403 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1405 ch = *text; // colors are ascii, so no u8_ needed
1406 if (ch <= '9' && ch >= '0') // ^[0-9] found
1408 colorindex = ch - '0';
1413 // i points to the char after ^...
1414 // i+3 points to 3 in ^x123
1415 // i+3 == *maxlen would mean that char is missing
1416 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1418 // building colorindex...
1419 ch = tolower(text[1]);
1420 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1421 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1422 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1423 else tempcolorindex = 0;
1426 ch = tolower(text[2]);
1427 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1428 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1429 else tempcolorindex = 0;
1432 ch = tolower(text[3]);
1433 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1434 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1435 else tempcolorindex = 0;
1438 colorindex = tempcolorindex | 0xf;
1439 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1447 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1456 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1463 map = ft2_oldstyle_map;
1465 if(!least_one || i0) // never skip the first character
1466 if(x + width_of[ch] * dw > maxwidth)
1469 break; // oops, can't draw this
1471 x += width_of[ch] * dw;
1473 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1475 map = FontMap_FindForChar(fontmap, ch);
1478 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1484 mapch = ch - map->start;
1485 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1487 x += map->glyphs[mapch].advance_x * dw;
1496 *outcolor = colorindex;
1501 float DrawQ_Color[4];
1502 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)
1504 int shadow, colorindex = STRING_COLOR_DEFAULT;
1506 float x = startx, y, s, t, u, v, thisw;
1507 float *av, *at, *ac;
1509 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1510 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1511 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1512 Uchar ch, mapch, nextch;
1513 Uchar prevch = 0; // used for kerning
1516 //ft2_font_map_t *prevmap = NULL; // the previous map
1517 ft2_font_map_t *map = NULL; // the currently used map
1518 ft2_font_map_t *fontmap = NULL; // the font map for the size
1520 const char *text_start = text;
1522 ft2_font_t *ft2 = fnt->ft2;
1523 qboolean snap = true;
1527 const float *width_of;
1530 tw = R_TextureWidth(fnt->tex);
1531 th = R_TextureHeight(fnt->tex);
1539 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1540 w *= fnt->settings.scale;
1541 h *= fnt->settings.scale;
1546 map_index = Font_IndexForSize(ft2, h, &w, &h);
1548 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1549 fontmap = Font_MapForIndex(ft2, map_index);
1555 // draw the font at its baseline when using freetype
1557 ftbase_y = dh * (4.5/6.0);
1562 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1563 if(!r_draw2d.integer && !r_draw2d_force)
1564 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1566 // R_Mesh_ResetTextureState();
1568 R_Mesh_TexBind(0, fnt->tex);
1569 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1576 //ftbase_x = snap_to_pixel_x(ftbase_x);
1579 startx = snap_to_pixel_x(startx, 0.4);
1580 starty = snap_to_pixel_y(starty, 0.4);
1581 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1584 pix_x = vid.width / vid_conwidth.value;
1585 pix_y = vid.height / vid_conheight.value;
1588 width_of = fontmap->width_of;
1590 width_of = fnt->width_of;
1592 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1597 if (!outcolor || *outcolor == -1)
1598 colorindex = STRING_COLOR_DEFAULT;
1600 colorindex = *outcolor;
1602 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1609 x += r_textshadow.value * vid.width / vid_conwidth.value;
1610 y += r_textshadow.value * vid.height / vid_conheight.value;
1613 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1615 nextch = ch = u8_getnchar(text, &text, bytes_left);
1616 i = text - text_start;
1619 if (ch == ' ' && !fontmap)
1621 x += width_of[(int) ' '] * dw;
1624 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1626 ch = *text; // colors are ascii, so no u8_ needed
1627 if (ch <= '9' && ch >= '0') // ^[0-9] found
1629 colorindex = ch - '0';
1630 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1635 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1637 // building colorindex...
1638 ch = tolower(text[1]);
1639 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1640 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1641 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1642 else tempcolorindex = 0;
1645 ch = tolower(text[2]);
1646 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1647 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1648 else tempcolorindex = 0;
1651 ch = tolower(text[3]);
1652 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1653 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1654 else tempcolorindex = 0;
1657 colorindex = tempcolorindex | 0xf;
1658 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1659 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1660 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1668 else if (ch == STRING_COLOR_TAG)
1677 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1678 // this way we don't need to rebind fnt->tex for every old-style character
1679 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1682 x += 1.0/pix_x * r_textshadow.value;
1683 y += 1.0/pix_y * r_textshadow.value;
1685 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1693 if (map != ft2_oldstyle_map)
1697 // switching from freetype to non-freetype rendering
1698 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1699 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1705 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1706 map = ft2_oldstyle_map;
1710 //num = (unsigned char) text[i];
1711 //thisw = fnt->width_of[num];
1712 thisw = fnt->width_of[ch];
1713 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1714 s = (ch & 15)*0.0625f + (0.5f / tw);
1715 t = (ch >> 4)*0.0625f + (0.5f / th);
1716 u = 0.0625f * thisw - (1.0f / tw);
1717 v = 0.0625f - (1.0f / th);
1718 ac[ 0] = DrawQ_Color[0];ac[ 1] = DrawQ_Color[1];ac[ 2] = DrawQ_Color[2];ac[ 3] = DrawQ_Color[3];
1719 ac[ 4] = DrawQ_Color[0];ac[ 5] = DrawQ_Color[1];ac[ 6] = DrawQ_Color[2];ac[ 7] = DrawQ_Color[3];
1720 ac[ 8] = DrawQ_Color[0];ac[ 9] = DrawQ_Color[1];ac[10] = DrawQ_Color[2];ac[11] = DrawQ_Color[3];
1721 ac[12] = DrawQ_Color[0];ac[13] = DrawQ_Color[1];ac[14] = DrawQ_Color[2];ac[15] = DrawQ_Color[3];
1722 at[ 0] = s ; at[ 1] = t ;
1723 at[ 2] = s+u ; at[ 3] = t ;
1724 at[ 4] = s+u ; at[ 5] = t+v ;
1725 at[ 6] = s ; at[ 7] = t+v ;
1726 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1727 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1728 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1729 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1734 if (batchcount >= QUADELEMENTS_MAXQUADS)
1736 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1737 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1743 x += width_of[ch] * dw;
1745 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1747 // new charmap - need to render
1750 // we need a different character map, render what we currently have:
1751 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1752 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1759 map = FontMap_FindForChar(fontmap, ch);
1762 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1769 // this shouldn't happen
1774 R_SetupShader_Generic(map->pic->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1777 mapch = ch - map->start;
1778 thisw = map->glyphs[mapch].advance_x;
1782 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1789 ac[ 0] = DrawQ_Color[0]; ac[ 1] = DrawQ_Color[1]; ac[ 2] = DrawQ_Color[2]; ac[ 3] = DrawQ_Color[3];
1790 ac[ 4] = DrawQ_Color[0]; ac[ 5] = DrawQ_Color[1]; ac[ 6] = DrawQ_Color[2]; ac[ 7] = DrawQ_Color[3];
1791 ac[ 8] = DrawQ_Color[0]; ac[ 9] = DrawQ_Color[1]; ac[10] = DrawQ_Color[2]; ac[11] = DrawQ_Color[3];
1792 ac[12] = DrawQ_Color[0]; ac[13] = DrawQ_Color[1]; ac[14] = DrawQ_Color[2]; ac[15] = DrawQ_Color[3];
1793 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1794 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1795 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1796 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1797 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1798 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1799 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1800 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1809 if (batchcount >= QUADELEMENTS_MAXQUADS)
1811 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1812 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1825 x -= 1.0/pix_x * r_textshadow.value;
1826 y -= 1.0/pix_y * r_textshadow.value;
1832 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1833 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1837 *outcolor = colorindex;
1839 // note: this relies on the proper text (not shadow) being drawn last
1843 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)
1845 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1848 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)
1850 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1853 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1855 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1858 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1860 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1865 // no ^xrgb management
1866 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1868 int color, numchars = 0;
1869 char *outputend2c = output2c + maxoutchars - 2;
1870 if (!outcolor || *outcolor == -1)
1871 color = STRING_COLOR_DEFAULT;
1875 maxreadchars = 1<<30;
1876 textend = text + maxreadchars;
1877 while (text != textend && *text)
1879 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1881 if (text[1] == STRING_COLOR_TAG)
1883 else if (text[1] >= '0' && text[1] <= '9')
1885 color = text[1] - '0';
1890 if (output2c >= outputend2c)
1892 *output2c++ = *text++;
1893 *output2c++ = color;
1896 output2c[0] = output2c[1] = 0;
1903 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)
1907 _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1908 if(!r_draw2d.integer && !r_draw2d_force)
1911 // R_Mesh_ResetTextureState();
1917 height = pic->height;
1918 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1921 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1923 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1924 floats[0] = floats[9] = x;
1925 floats[1] = floats[4] = y;
1926 floats[3] = floats[6] = x + width;
1927 floats[7] = floats[10] = y + height;
1928 floats[12] = s1;floats[13] = t1;
1929 floats[14] = s2;floats[15] = t2;
1930 floats[16] = s4;floats[17] = t4;
1931 floats[18] = s3;floats[19] = t3;
1932 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1933 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1934 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1935 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1937 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1938 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1941 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1944 if(!r_draw2d.integer && !r_draw2d_force)
1946 DrawQ_ProcessDrawFlag(flags, hasalpha);
1948 // R_Mesh_ResetTextureState();
1949 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1951 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1952 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1955 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1959 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1960 if(!r_draw2d.integer && !r_draw2d_force)
1964 switch(vid.renderpath)
1966 case RENDERPATH_GL11:
1967 case RENDERPATH_GL13:
1968 case RENDERPATH_GL20:
1971 qglBegin(GL_LINE_LOOP);
1972 for (num = 0;num < mesh->num_vertices;num++)
1974 if (mesh->data_color4f)
1975 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]);
1976 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1982 case RENDERPATH_D3D9:
1983 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1985 case RENDERPATH_D3D10:
1986 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1988 case RENDERPATH_D3D11:
1989 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1991 case RENDERPATH_SOFT:
1992 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1994 case RENDERPATH_GLES1:
1995 case RENDERPATH_GLES2:
1996 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2001 //[515]: this is old, delete
2002 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
2004 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
2005 if(!r_draw2d.integer && !r_draw2d_force)
2008 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
2010 switch(vid.renderpath)
2012 case RENDERPATH_GL11:
2013 case RENDERPATH_GL13:
2014 case RENDERPATH_GL20:
2018 //qglLineWidth(width);CHECKGLERROR
2020 GL_Color(r,g,b,alpha);
2023 qglVertex2f(x1, y1);
2024 qglVertex2f(x2, y2);
2029 case RENDERPATH_D3D9:
2030 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2032 case RENDERPATH_D3D10:
2033 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2035 case RENDERPATH_D3D11:
2036 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2038 case RENDERPATH_SOFT:
2039 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2041 case RENDERPATH_GLES1:
2042 case RENDERPATH_GLES2:
2043 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2048 void DrawQ_Lines (float width, int numlines, const float *vertex3f, const float *color4f, int flags)
2051 qboolean hasalpha = false;
2052 for (i = 0;i < numlines*2;i++)
2053 if (color4f[i*4+3] < 1.0f)
2056 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, hasalpha ? 0.5f : 1.0f);
2058 if(!r_draw2d.integer && !r_draw2d_force)
2061 switch(vid.renderpath)
2063 case RENDERPATH_GL11:
2064 case RENDERPATH_GL13:
2065 case RENDERPATH_GL20:
2068 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
2070 //qglLineWidth(width);CHECKGLERROR
2073 R_Mesh_PrepareVertices_Generic_Arrays(numlines*2, vertex3f, color4f, NULL);
2074 qglDrawArrays(GL_LINES, 0, numlines*2);
2077 case RENDERPATH_D3D9:
2078 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2080 case RENDERPATH_D3D10:
2081 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2083 case RENDERPATH_D3D11:
2084 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2086 case RENDERPATH_SOFT:
2087 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2089 case RENDERPATH_GLES1:
2090 case RENDERPATH_GLES2:
2091 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2096 void DrawQ_SetClipArea(float x, float y, float width, float height)
2101 // We have to convert the con coords into real coords
2102 // OGL uses top to bottom
2103 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
2104 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
2105 iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
2106 ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
2107 switch(vid.renderpath)
2109 case RENDERPATH_GL11:
2110 case RENDERPATH_GL13:
2111 case RENDERPATH_GL20:
2112 case RENDERPATH_GLES1:
2113 case RENDERPATH_GLES2:
2114 case RENDERPATH_SOFT:
2115 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
2117 case RENDERPATH_D3D9:
2118 GL_Scissor(ix, iy, iw, ih);
2120 case RENDERPATH_D3D10:
2121 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2123 case RENDERPATH_D3D11:
2124 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2128 GL_ScissorTest(true);
2131 void DrawQ_ResetClipArea(void)
2134 GL_ScissorTest(false);
2137 void DrawQ_Finish(void)
2139 r_refdef.draw2dstage = 0;
2142 void DrawQ_RecalcView(void)
2144 if(r_refdef.draw2dstage)
2145 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2148 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2149 void R_DrawGamma(void)
2152 switch(vid.renderpath)
2154 case RENDERPATH_GL20:
2155 case RENDERPATH_D3D9:
2156 case RENDERPATH_D3D10:
2157 case RENDERPATH_D3D11:
2158 case RENDERPATH_GLES2:
2159 if (vid_usinghwgamma || v_glslgamma.integer)
2162 case RENDERPATH_GL11:
2163 case RENDERPATH_GL13:
2164 if (vid_usinghwgamma)
2167 case RENDERPATH_GLES1:
2168 case RENDERPATH_SOFT:
2171 // all the blends ignore depth
2172 // R_Mesh_ResetTextureState();
2173 R_SetupShader_Generic_NoTexture(true, true);
2175 GL_DepthRange(0, 1);
2176 GL_PolygonOffset(0, 0);
2177 GL_DepthTest(false);
2179 // interpretation of brightness and contrast:
2180 // color range := brightness .. (brightness + contrast)
2181 // i.e. "c *= contrast; c += brightness"
2182 // plausible values for brightness thus range from -contrast to 1
2184 // apply pre-brightness (subtractive brightness, for where contrast was >= 1)
2185 if (vid.support.ext_blend_subtract)
2187 if (v_color_enable.integer)
2189 c[0] = -v_color_black_r.value / v_color_white_r.value;
2190 c[1] = -v_color_black_g.value / v_color_white_g.value;
2191 c[2] = -v_color_black_b.value / v_color_white_b.value;
2194 c[0] = c[1] = c[2] = -v_brightness.value / v_contrast.value;
2195 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2197 // need SUBTRACTIVE blending to do this!
2198 GL_BlendEquationSubtract(true);
2199 GL_BlendFunc(GL_ONE, GL_ONE);
2200 GL_Color(c[0], c[1], c[2], 1);
2201 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2202 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2203 GL_BlendEquationSubtract(false);
2208 if (v_color_enable.integer)
2210 c[0] = v_color_white_r.value;
2211 c[1] = v_color_white_g.value;
2212 c[2] = v_color_white_b.value;
2215 c[0] = c[1] = c[2] = v_contrast.value;
2216 if (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2218 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2219 while (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2222 cc[0] = bound(0, c[0] - 1, 1);
2223 cc[1] = bound(0, c[1] - 1, 1);
2224 cc[2] = bound(0, c[2] - 1, 1);
2225 GL_Color(cc[0], cc[1], cc[2], 1);
2226 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2227 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2233 if (c[0] <= 0.997f || c[1] <= 0.997f || c[2] <= 0.997f)
2235 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
2236 GL_Color(c[0], c[1], c[2], 1);
2237 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2238 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2241 // apply post-brightness (additive brightness, for where contrast was <= 1)
2242 if (v_color_enable.integer)
2244 c[0] = v_color_black_r.value;
2245 c[1] = v_color_black_g.value;
2246 c[2] = v_color_black_b.value;
2249 c[0] = c[1] = c[2] = v_brightness.value;
2250 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2252 GL_BlendFunc(GL_ONE, GL_ONE);
2253 GL_Color(c[0], c[1], c[2], 1);
2254 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2255 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);