2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 #include "cl_dyntexture.h"
29 #include "ft2_fontdefs.h"
32 static mempool_t *fonts_mempool = NULL;
34 cvar_t r_textshadow = {CVAR_SAVE, "r_textshadow", "0", "draws a shadow on all text to improve readability (note: value controls offset, 1 = 1 pixel, 1.5 = 1.5 pixels, etc)"};
35 cvar_t r_textbrightness = {CVAR_SAVE, "r_textbrightness", "0", "additional brightness for text color codes (0 keeps colors as is, 1 makes them all white)"};
36 cvar_t r_textcontrast = {CVAR_SAVE, "r_textcontrast", "1", "additional contrast for text color codes (1 keeps colors as is, 0 makes them all black)"};
38 cvar_t r_font_postprocess_blur = {CVAR_SAVE, "r_font_postprocess_blur", "0", "font blur amount"};
39 cvar_t r_font_postprocess_outline = {CVAR_SAVE, "r_font_postprocess_outline", "0", "font outline amount"};
40 cvar_t r_font_postprocess_shadow_x = {CVAR_SAVE, "r_font_postprocess_shadow_x", "0", "font shadow X shift amount, applied during outlining"};
41 cvar_t r_font_postprocess_shadow_y = {CVAR_SAVE, "r_font_postprocess_shadow_y", "0", "font shadow Y shift amount, applied during outlining"};
42 cvar_t r_font_postprocess_shadow_z = {CVAR_SAVE, "r_font_postprocess_shadow_z", "0", "font shadow Z shift amount, applied during blurring"};
43 cvar_t r_font_hinting = {CVAR_SAVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"};
44 cvar_t r_font_antialias = {CVAR_SAVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */};
46 extern cvar_t v_glslgamma;
48 //=============================================================================
49 /* Support Routines */
51 #define FONT_FILESIZE 13468
52 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
53 static cachepic_t cachepics[MAX_CACHED_PICS];
54 static int numcachepics;
56 rtexturepool_t *drawtexturepool;
58 static const unsigned char concharimage[FONT_FILESIZE] =
63 static rtexture_t *draw_generateconchars(void)
70 data = LoadTGA_BGRA (concharimage, FONT_FILESIZE, NULL);
72 for (i = 0;i < 8192;i++)
74 random = lhrandom (0.0,1.0);
75 data[i*4+3] = data[i*4+0];
76 data[i*4+2] = 83 + (unsigned char)(random * 64);
77 data[i*4+1] = 71 + (unsigned char)(random * 32);
78 data[i*4+0] = 23 + (unsigned char)(random * 16);
81 for (i = 8192;i < 32768;i++)
83 random = lhrandom (0.0,1.0);
84 data[i*4+3] = data[i*4+0];
85 data[i*4+2] = 95 + (unsigned char)(random * 64);
86 data[i*4+1] = 95 + (unsigned char)(random * 64);
87 data[i*4+0] = 95 + (unsigned char)(random * 64);
90 for (i = 32768;i < 40960;i++)
92 random = lhrandom (0.0,1.0);
93 data[i*4+3] = data[i*4+0];
94 data[i*4+2] = 83 + (unsigned char)(random * 64);
95 data[i*4+1] = 71 + (unsigned char)(random * 32);
96 data[i*4+0] = 23 + (unsigned char)(random * 16);
99 for (i = 40960;i < 65536;i++)
101 random = lhrandom (0.0,1.0);
102 data[i*4+3] = data[i*4+0];
103 data[i*4+2] = 96 + (unsigned char)(random * 64);
104 data[i*4+1] = 43 + (unsigned char)(random * 32);
105 data[i*4+0] = 27 + (unsigned char)(random * 32);
109 Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, data);
112 tex = R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, data, TEXTYPE_BGRA, TEXF_ALPHA, -1, NULL);
117 static rtexture_t *draw_generateditherpattern(void)
120 unsigned char pixels[8][8];
121 for (y = 0;y < 8;y++)
122 for (x = 0;x < 8;x++)
123 pixels[y][x] = ((x^y) & 4) ? 254 : 0;
124 return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST, -1, palette_bgra_transparent);
127 typedef struct embeddedpic_s
136 static const embeddedpic_t embeddedpics[] =
139 "gfx/prydoncursor001", 16, 16,
158 "ui/mousepointer", 16, 16,
177 "gfx/crosshair1", 16, 16,
196 "gfx/crosshair2", 16, 16,
215 "gfx/crosshair3", 16, 16,
234 "gfx/crosshair4", 16, 16,
253 "gfx/crosshair5", 8, 8,
264 "gfx/crosshair6", 2, 2,
269 "gfx/crosshair7", 16, 16,
290 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
292 const embeddedpic_t *p;
293 for (p = embeddedpics;p->name;p++)
294 if (!strcmp(name, p->name))
295 return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA, -1, palette_bgra_embeddedpic);
296 if (!strcmp(name, "gfx/conchars"))
297 return draw_generateconchars();
298 if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
299 return draw_generateditherpattern();
301 Con_DPrintf("Draw_CachePic: failed to load %s\n", name);
302 return r_texture_notexture;
312 // FIXME: move this to client somehow
313 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
316 unsigned char *pixels = NULL;
319 unsigned char *lmpdata;
320 char lmpname[MAX_QPATH];
323 qboolean ddshasalpha;
324 float ddsavgcolor[4];
325 qboolean loaded = false;
327 texflags = TEXF_ALPHA;
328 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
329 texflags |= TEXF_CLAMP;
330 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer && gl_texturecompression.integer)
331 texflags |= TEXF_COMPRESS;
333 // check whether the picture has already been cached
334 crc = CRC_Block((unsigned char *)path, strlen(path));
335 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
336 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
338 if (!strcmp (path, pic->name))
340 // if it was created (or replaced) by Draw_NewPic, just return it
341 if(pic->flags & CACHEPICFLAG_NEWPIC)
343 if (!((pic->texflags ^ texflags) & ~(TEXF_COMPRESS))) // ignore TEXF_COMPRESS when comparing, because fallback pics remove the flag
345 if(!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
348 pic->autoload = false; // persist it
350 goto reload; // load it below, and then persist
357 if (numcachepics == MAX_CACHED_PICS)
359 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
360 // FIXME: support NULL in callers?
361 return cachepics; // return the first one
363 pic = cachepics + (numcachepics++);
364 memset(pic, 0, sizeof(*pic));
365 strlcpy (pic->name, path, sizeof(pic->name));
367 pic->chain = cachepichash[hashkey];
368 cachepichash[hashkey] = pic;
371 // TODO why does this crash?
372 if(pic->allow_free_tex && pic->tex)
373 R_PurgeTexture(pic->tex);
375 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
376 pic->flags = cachepicflags;
377 pic->tex = CL_GetDynTexture( path );
378 // if so, set the width/height, too
380 pic->allow_free_tex = false;
381 pic->width = R_TextureWidth(pic->tex);
382 pic->height = R_TextureHeight(pic->tex);
383 // we're done now (early-out)
387 pic->allow_free_tex = true;
389 pic->hasalpha = true; // assume alpha unless we know it has none
390 pic->texflags = texflags;
391 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
392 pic->lastusedframe = draw_frame;
394 // load a high quality image from disk if possible
395 if (!loaded && r_texture_dds_load.integer != 0 && (pic->tex = R_LoadTextureDDSFile(drawtexturepool, va("dds/%s.dds", pic->name), pic->texflags, &ddshasalpha, ddsavgcolor, 0)))
397 // note this loads even if autoload is true, otherwise we can't get the width/height
399 pic->hasalpha = ddshasalpha;
400 pic->width = R_TextureWidth(pic->tex);
401 pic->height = R_TextureHeight(pic->tex);
403 if (!loaded && ((pixels = loadimagepixelsbgra(pic->name, false, true, false, NULL)) || (!strncmp(pic->name, "gfx/", 4) && (pixels = loadimagepixelsbgra(pic->name+4, false, true, false, NULL)))))
406 pic->hasalpha = false;
407 if (pic->texflags & TEXF_ALPHA)
409 for (j = 3;j < image_width * image_height * 4;j += 4)
413 pic->hasalpha = true;
419 pic->width = image_width;
420 pic->height = image_height;
423 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);
425 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
426 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
432 pic->autoload = false;
433 // never compress the fallback images
434 pic->texflags &= ~TEXF_COMPRESS;
437 // now read the low quality version (wad or lmp file), and take the pic
438 // size from that even if we don't upload the texture, this way the pics
439 // show up the right size in the menu even if they were replaced with
440 // higher or lower resolution versions
441 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", pic->name);
442 if (!strncmp(pic->name, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
444 if (developer_loading.integer)
445 Con_Printf("loading lump \"%s\"\n", pic->name);
449 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
450 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
451 // if no high quality replacement image was found, upload the original low quality texture
455 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);
460 else if ((lmpdata = W_GetLumpName (pic->name + 4)))
462 if (developer_loading.integer)
463 Con_Printf("loading gfx.wad lump \"%s\"\n", pic->name + 4);
465 if (!strcmp(pic->name, "gfx/conchars"))
467 // conchars is a raw image and with color 0 as transparent instead of 255
470 // if no high quality replacement image was found, upload the original low quality texture
474 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, 128, 128, lmpdata, vid.sRGB2D != 0 ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
479 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
480 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
481 // if no high quality replacement image was found, upload the original low quality texture
485 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);
497 // if it's not found on disk, generate an image
498 pic->tex = draw_generatepic(pic->name, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
499 pic->width = R_TextureWidth(pic->tex);
500 pic->height = R_TextureHeight(pic->tex);
501 pic->allow_free_tex = (pic->tex != r_texture_notexture);
507 cachepic_t *Draw_CachePic (const char *path)
509 return Draw_CachePic_Flags (path, 0); // default to persistent!
512 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
514 if (pic->autoload && !pic->tex)
516 if (pic->tex == NULL && r_texture_dds_load.integer != 0)
518 qboolean ddshasalpha;
519 float ddsavgcolor[4];
520 pic->tex = R_LoadTextureDDSFile(drawtexturepool, va("dds/%s.dds", pic->name), pic->texflags, &ddshasalpha, ddsavgcolor, 0);
522 if (pic->tex == NULL)
524 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, vid.sRGB2D);
526 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
527 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
530 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
532 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, vid.sRGB2D);
534 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
535 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
538 if (pic->tex == NULL)
539 pic->tex = draw_generatepic(pic->name, true);
541 pic->lastusedframe = draw_frame;
545 void Draw_Frame(void)
549 static double nextpurgetime;
550 if (nextpurgetime > realtime)
552 nextpurgetime = realtime + 0.05;
553 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
555 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
557 R_FreeTexture(pic->tex);
564 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
569 crc = CRC_Block((unsigned char *)picname, strlen(picname));
570 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
571 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
572 if (!strcmp (picname, pic->name))
577 if (pic->flags == CACHEPICFLAG_NEWPIC && pic->tex && pic->width == width && pic->height == height)
579 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, 0, width, height, 1);
585 if (numcachepics == MAX_CACHED_PICS)
587 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
588 // FIXME: support NULL in callers?
589 return cachepics; // return the first one
591 pic = cachepics + (numcachepics++);
592 memset(pic, 0, sizeof(*pic));
593 strlcpy (pic->name, picname, sizeof(pic->name));
595 pic->chain = cachepichash[hashkey];
596 cachepichash[hashkey] = pic;
599 pic->flags = CACHEPICFLAG_NEWPIC; // disable texflags checks in Draw_CachePic
601 pic->height = height;
602 if (pic->allow_free_tex && pic->tex)
603 R_FreeTexture(pic->tex);
604 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0), -1, NULL);
608 void Draw_FreePic(const char *picname)
613 // this doesn't really free the pic, but does free it's texture
614 crc = CRC_Block((unsigned char *)picname, strlen(picname));
615 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
616 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
618 if (!strcmp (picname, pic->name) && pic->tex)
620 R_FreeTexture(pic->tex);
629 static float snap_to_pixel_x(float x, float roundUpAt);
630 extern int con_linewidth; // to force rewrapping
631 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
635 char widthfile[MAX_QPATH];
637 fs_offset_t widthbufsize;
639 if(override || !fnt->texpath[0])
641 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
642 // load the cvars when the font is FIRST loader
643 fnt->settings.scale = scale;
644 fnt->settings.voffset = voffset;
645 fnt->settings.antialias = r_font_antialias.integer;
646 fnt->settings.hinting = r_font_hinting.integer;
647 fnt->settings.outline = r_font_postprocess_outline.value;
648 fnt->settings.blur = r_font_postprocess_blur.value;
649 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
650 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
651 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
654 if (fnt->settings.scale <= 0)
655 fnt->settings.scale = 1;
657 if(drawtexturepool == NULL)
658 return; // before gl_draw_start, so will be loaded later
662 // clear freetype font
663 Font_UnloadFont(fnt->ft2);
668 if(fnt->req_face != -1)
670 if(!Font_LoadFont(fnt->texpath, fnt))
671 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
674 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
675 if(fnt->tex == r_texture_notexture)
677 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
679 if (!fnt->fallbacks[i][0])
681 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
682 if(fnt->tex != r_texture_notexture)
685 if(fnt->tex == r_texture_notexture)
687 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
688 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
691 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
694 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
696 // unspecified width == 1 (base width)
697 for(ch = 0; ch < 256; ++ch)
698 fnt->width_of[ch] = 1;
700 // FIXME load "name.width", if it fails, fill all with 1
701 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
703 float extraspacing = 0;
704 const char *p = widthbuf;
709 if(!COM_ParseToken_Simple(&p, false, false))
727 fnt->width_of[ch] = atof(com_token) + extraspacing;
731 if(!strcmp(com_token, "extraspacing"))
733 if(!COM_ParseToken_Simple(&p, false, false))
735 extraspacing = atof(com_token);
737 else if(!strcmp(com_token, "scale"))
739 if(!COM_ParseToken_Simple(&p, false, false))
741 fnt->settings.scale = atof(com_token);
745 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
746 if(!COM_ParseToken_Simple(&p, false, false))
758 for (i = 0; i < MAX_FONT_SIZES; ++i)
760 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
763 for(ch = 0; ch < 256; ++ch)
764 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
768 maxwidth = fnt->width_of[0];
769 for(i = 1; i < 256; ++i)
770 maxwidth = max(maxwidth, fnt->width_of[i]);
771 fnt->maxwidth = maxwidth;
773 // fix up maxwidth for overlap
774 fnt->maxwidth *= fnt->settings.scale;
776 if(fnt == FONT_CONSOLE)
777 con_linewidth = -1; // rewrap console in next frame
780 extern cvar_t developer_font;
781 dp_font_t *FindFont(const char *title, qboolean allocate_new)
786 for(i = 0; i < dp_fonts.maxsize; ++i)
787 if(!strcmp(dp_fonts.f[i].title, title))
788 return &dp_fonts.f[i];
789 // if not found - try allocate
792 // find any font with empty title
793 for(i = 0; i < dp_fonts.maxsize; ++i)
795 if(!strcmp(dp_fonts.f[i].title, ""))
797 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
798 return &dp_fonts.f[i];
801 // if no any 'free' fonts - expand buffer
802 oldsize = dp_fonts.maxsize;
803 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
804 if (developer_font.integer)
805 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", oldsize, dp_fonts.maxsize);
806 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
807 // relink ft2 structures
808 for(i = 0; i < oldsize; ++i)
809 if (dp_fonts.f[i].ft2)
810 dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings;
811 // register a font in first expanded slot
812 strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
813 return &dp_fonts.f[oldsize];
818 static float snap_to_pixel_x(float x, float roundUpAt)
820 float pixelpos = x * vid.width / vid_conwidth.value;
821 int snap = (int) pixelpos;
822 if (pixelpos - snap >= roundUpAt) ++snap;
823 return ((float)snap * vid_conwidth.value / vid.width);
825 x = (int)(x * vid.width / vid_conwidth.value);
826 x = (x * vid_conwidth.value / vid.width);
831 static float snap_to_pixel_y(float y, float roundUpAt)
833 float pixelpos = y * vid.height / vid_conheight.value;
834 int snap = (int) pixelpos;
835 if (pixelpos - snap > roundUpAt) ++snap;
836 return ((float)snap * vid_conheight.value / vid.height);
838 y = (int)(y * vid.height / vid_conheight.value);
839 y = (y * vid_conheight.value / vid.height);
844 static void LoadFont_f(void)
848 const char *filelist, *c, *cm;
849 float sz, scale, voffset;
850 char mainfont[MAX_QPATH];
854 Con_Printf("Available font commands:\n");
855 for(i = 0; i < dp_fonts.maxsize; ++i)
856 if (dp_fonts.f[i].title[0])
857 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
858 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
859 "can specify multiple fonts and faces\n"
860 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
861 "to load face 2 of the font gfx/vera-sans and use face 1\n"
862 "of gfx/fallback as fallback font.\n"
863 "You can also specify a list of font sizes to load, like this:\n"
864 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
865 "In many cases, 8 12 16 24 32 should be a good choice.\n"
867 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
868 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
872 f = FindFont(Cmd_Argv(1), true);
875 Con_Printf("font function not found\n");
880 filelist = "gfx/conchars";
882 filelist = Cmd_Argv(2);
884 memset(f->fallbacks, 0, sizeof(f->fallbacks));
885 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
887 // first font is handled "normally"
888 c = strchr(filelist, ':');
889 cm = strchr(filelist, ',');
890 if(c && (!cm || c < cm))
891 f->req_face = atoi(c+1);
898 if(!c || (c - filelist) > MAX_QPATH)
899 strlcpy(mainfont, filelist, sizeof(mainfont));
902 memcpy(mainfont, filelist, c - filelist);
903 mainfont[c - filelist] = 0;
906 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
908 c = strchr(filelist, ',');
914 c = strchr(filelist, ':');
915 cm = strchr(filelist, ',');
916 if(c && (!cm || c < cm))
917 f->fallback_faces[i] = atoi(c+1);
920 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
923 if(!c || (c-filelist) > MAX_QPATH)
925 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
929 memcpy(f->fallbacks[i], filelist, c - filelist);
930 f->fallbacks[i][c - filelist] = 0;
934 // for now: by default load only one size: the default size
936 for(i = 1; i < MAX_FONT_SIZES; ++i)
937 f->req_sizes[i] = -1;
943 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
946 if (!strcmp(Cmd_Argv(i), "scale"))
950 scale = atof(Cmd_Argv(i));
953 if (!strcmp(Cmd_Argv(i), "voffset"))
957 voffset = atof(Cmd_Argv(i));
962 continue; // no slot for other sizes
964 // parse one of sizes
965 sz = atof(Cmd_Argv(i));
966 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
968 // search for duplicated sizes
970 for (j=0; j<sizes; j++)
971 if (f->req_sizes[j] == sz)
974 continue; // sz already in req_sizes, don't add it again
976 if (sizes == MAX_FONT_SIZES)
978 Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
982 f->req_sizes[sizes] = sz;
988 LoadFont(true, mainfont, f, scale, voffset);
996 static void gl_draw_start(void)
999 drawtexturepool = R_AllocTexturePool();
1002 memset(cachepichash, 0, sizeof(cachepichash));
1006 // load default font textures
1007 for(i = 0; i < dp_fonts.maxsize; ++i)
1008 if (dp_fonts.f[i].title[0])
1009 LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
1011 // draw the loading screen so people have something to see in the newly opened window
1012 SCR_UpdateLoadingScreen(true);
1015 static void gl_draw_shutdown(void)
1019 R_FreeTexturePool(&drawtexturepool);
1022 memset(cachepichash, 0, sizeof(cachepichash));
1025 static void gl_draw_newmap(void)
1030 void GL_Draw_Init (void)
1034 Cvar_RegisterVariable(&r_font_postprocess_blur);
1035 Cvar_RegisterVariable(&r_font_postprocess_outline);
1036 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
1037 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
1038 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
1039 Cvar_RegisterVariable(&r_font_hinting);
1040 Cvar_RegisterVariable(&r_font_antialias);
1041 Cvar_RegisterVariable(&r_textshadow);
1042 Cvar_RegisterVariable(&r_textbrightness);
1043 Cvar_RegisterVariable(&r_textcontrast);
1045 // allocate fonts storage
1046 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
1047 dp_fonts.maxsize = MAX_FONTS;
1048 dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
1049 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
1051 // assign starting font names
1052 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
1053 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
1054 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
1055 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
1056 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
1057 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
1058 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
1059 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
1060 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
1061 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
1062 if(!FONT_USER(i)->title[0])
1063 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
1065 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
1066 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
1069 static void _DrawQ_Setup(void)
1071 r_viewport_t viewport;
1072 if (r_refdef.draw2dstage == 1)
1074 r_refdef.draw2dstage = 1;
1076 R_Viewport_InitOrtho(&viewport, &identitymatrix, r_refdef.view.x, vid.height - r_refdef.view.y - r_refdef.view.height, r_refdef.view.width, r_refdef.view.height, 0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100, NULL);
1077 R_Mesh_SetRenderTargets(0, NULL, NULL, NULL, NULL, NULL);
1078 R_SetViewport(&viewport);
1079 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
1080 GL_DepthFunc(GL_LEQUAL);
1081 GL_PolygonOffset(0,0);
1082 GL_CullFace(GL_NONE);
1083 R_EntityMatrix(&identitymatrix);
1085 GL_DepthRange(0, 1);
1086 GL_PolygonOffset(0, 0);
1087 GL_DepthTest(false);
1091 qboolean r_draw2d_force = false;
1092 void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1096 if(!r_draw2d.integer && !r_draw2d_force)
1098 DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1100 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1102 if(flags == DRAWFLAG_ADDITIVE)
1104 GL_DepthMask(false);
1105 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1107 else if(flags == DRAWFLAG_MODULATE)
1109 GL_DepthMask(false);
1110 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1112 else if(flags == DRAWFLAG_2XMODULATE)
1114 GL_DepthMask(false);
1115 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1117 else if(flags == DRAWFLAG_SCREEN)
1119 GL_DepthMask(false);
1120 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1124 GL_DepthMask(false);
1125 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1130 GL_BlendFunc(GL_ONE, GL_ZERO);
1134 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1138 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1139 if(!r_draw2d.integer && !r_draw2d_force)
1142 // R_Mesh_ResetTextureState();
1143 floats[12] = 0.0f;floats[13] = 0.0f;
1144 floats[14] = 1.0f;floats[15] = 0.0f;
1145 floats[16] = 1.0f;floats[17] = 1.0f;
1146 floats[18] = 0.0f;floats[19] = 1.0f;
1147 floats[20] = floats[24] = floats[28] = floats[32] = red;
1148 floats[21] = floats[25] = floats[29] = floats[33] = green;
1149 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1150 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1156 height = pic->height;
1157 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1160 // AK07: lets be texel correct on the corners
1162 float horz_offset = 0.5f / pic->width;
1163 float vert_offset = 0.5f / pic->height;
1165 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1166 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1167 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1168 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1173 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1175 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1176 floats[0] = floats[9] = x;
1177 floats[1] = floats[4] = y;
1178 floats[3] = floats[6] = x + width;
1179 floats[7] = floats[10] = y + height;
1181 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1182 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1185 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)
1188 float af = DEG2RAD(-angle); // forward
1189 float ar = DEG2RAD(-angle + 90); // right
1190 float sinaf = sin(af);
1191 float cosaf = cos(af);
1192 float sinar = sin(ar);
1193 float cosar = cos(ar);
1195 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1196 if(!r_draw2d.integer && !r_draw2d_force)
1199 // R_Mesh_ResetTextureState();
1205 height = pic->height;
1206 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1209 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1211 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1214 floats[0] = x - cosaf*org_x - cosar*org_y;
1215 floats[1] = y - sinaf*org_x - sinar*org_y;
1218 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1219 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1222 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1223 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1226 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1227 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1229 floats[12] = 0.0f;floats[13] = 0.0f;
1230 floats[14] = 1.0f;floats[15] = 0.0f;
1231 floats[16] = 1.0f;floats[17] = 1.0f;
1232 floats[18] = 0.0f;floats[19] = 1.0f;
1233 floats[20] = floats[24] = floats[28] = floats[32] = red;
1234 floats[21] = floats[25] = floats[29] = floats[33] = green;
1235 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1236 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1238 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1239 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1242 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1246 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1247 if(!r_draw2d.integer && !r_draw2d_force)
1250 // R_Mesh_ResetTextureState();
1251 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1253 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1254 floats[0] = floats[9] = x;
1255 floats[1] = floats[4] = y;
1256 floats[3] = floats[6] = x + width;
1257 floats[7] = floats[10] = y + height;
1258 floats[12] = 0.0f;floats[13] = 0.0f;
1259 floats[14] = 1.0f;floats[15] = 0.0f;
1260 floats[16] = 1.0f;floats[17] = 1.0f;
1261 floats[18] = 0.0f;floats[19] = 1.0f;
1262 floats[20] = floats[24] = floats[28] = floats[32] = red;
1263 floats[21] = floats[25] = floats[29] = floats[33] = green;
1264 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1265 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1267 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1268 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1271 /// color tag printing
1272 static const vec4_t string_colors[] =
1275 // LordHavoc: why on earth is cyan before magenta in Quake3?
1276 // LordHavoc: note: Doom3 uses white for [0] and [7]
1277 {0.0, 0.0, 0.0, 1.0}, // black
1278 {1.0, 0.0, 0.0, 1.0}, // red
1279 {0.0, 1.0, 0.0, 1.0}, // green
1280 {1.0, 1.0, 0.0, 1.0}, // yellow
1281 {0.0, 0.0, 1.0, 1.0}, // blue
1282 {0.0, 1.0, 1.0, 1.0}, // cyan
1283 {1.0, 0.0, 1.0, 1.0}, // magenta
1284 {1.0, 1.0, 1.0, 1.0}, // white
1285 // [515]'s BX_COLOREDTEXT extension
1286 {1.0, 1.0, 1.0, 0.5}, // half transparent
1287 {0.5, 0.5, 0.5, 1.0} // half brightness
1288 // Black's color table
1289 //{1.0, 1.0, 1.0, 1.0},
1290 //{1.0, 0.0, 0.0, 1.0},
1291 //{0.0, 1.0, 0.0, 1.0},
1292 //{0.0, 0.0, 1.0, 1.0},
1293 //{1.0, 1.0, 0.0, 1.0},
1294 //{0.0, 1.0, 1.0, 1.0},
1295 //{1.0, 0.0, 1.0, 1.0},
1296 //{0.1, 0.1, 0.1, 1.0}
1299 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1301 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1303 float C = r_textcontrast.value;
1304 float B = r_textbrightness.value;
1305 if (colorindex & 0x10000) // that bit means RGB color
1307 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1308 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1309 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1310 color[3] = (colorindex & 0xf) / 15.0;
1313 Vector4Copy(string_colors[colorindex], color);
1314 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1317 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1318 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1322 // NOTE: this function always draws exactly one character if maxwidth <= 0
1323 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)
1325 const char *text_start = text;
1326 int colorindex = STRING_COLOR_DEFAULT;
1329 Uchar ch, mapch, nextch;
1330 Uchar prevch = 0; // used for kerning
1335 ft2_font_map_t *fontmap = NULL;
1336 ft2_font_map_t *map = NULL;
1337 //ft2_font_map_t *prevmap = NULL;
1338 ft2_font_t *ft2 = fnt->ft2;
1340 qboolean snap = true;
1341 qboolean least_one = false;
1342 float dw; // display w
1343 //float dh; // display h
1344 const float *width_of;
1351 // do this in the end
1352 w *= fnt->settings.scale;
1353 h *= fnt->settings.scale;
1355 // find the most fitting size:
1359 map_index = Font_IndexForSize(ft2, h, &w, &h);
1361 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1362 fontmap = Font_MapForIndex(ft2, map_index);
1371 if (!outcolor || *outcolor == -1)
1372 colorindex = STRING_COLOR_DEFAULT;
1374 colorindex = *outcolor;
1376 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1377 // ftbase_x = snap_to_pixel_x(0);
1382 maxwidth = -maxwidth;
1386 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1389 width_of = fontmap->width_of;
1391 width_of = fnt->width_of;
1393 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1396 nextch = ch = u8_getnchar(text, &text, bytes_left);
1397 i = text - text_start;
1400 if (ch == ' ' && !fontmap)
1402 if(!least_one || i0) // never skip the first character
1403 if(x + width_of[(int) ' '] * dw > maxwidth)
1406 break; // oops, can't draw this
1408 x += width_of[(int) ' '] * dw;
1411 // i points to the char after ^
1412 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1414 ch = *text; // colors are ascii, so no u8_ needed
1415 if (ch <= '9' && ch >= '0') // ^[0-9] found
1417 colorindex = ch - '0';
1422 // i points to the char after ^...
1423 // i+3 points to 3 in ^x123
1424 // i+3 == *maxlen would mean that char is missing
1425 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1427 // building colorindex...
1428 ch = tolower(text[1]);
1429 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1430 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1431 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1432 else tempcolorindex = 0;
1435 ch = tolower(text[2]);
1436 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1437 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1438 else tempcolorindex = 0;
1441 ch = tolower(text[3]);
1442 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1443 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1444 else tempcolorindex = 0;
1447 colorindex = tempcolorindex | 0xf;
1448 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1456 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1465 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1472 map = ft2_oldstyle_map;
1474 if(!least_one || i0) // never skip the first character
1475 if(x + width_of[ch] * dw > maxwidth)
1478 break; // oops, can't draw this
1480 x += width_of[ch] * dw;
1482 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1484 map = FontMap_FindForChar(fontmap, ch);
1487 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1493 mapch = ch - map->start;
1494 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1496 x += map->glyphs[mapch].advance_x * dw;
1505 *outcolor = colorindex;
1510 float DrawQ_Color[4];
1511 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)
1513 int shadow, colorindex = STRING_COLOR_DEFAULT;
1515 float x = startx, y, s, t, u, v, thisw;
1516 float *av, *at, *ac;
1518 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1519 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1520 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1521 Uchar ch, mapch, nextch;
1522 Uchar prevch = 0; // used for kerning
1525 //ft2_font_map_t *prevmap = NULL; // the previous map
1526 ft2_font_map_t *map = NULL; // the currently used map
1527 ft2_font_map_t *fontmap = NULL; // the font map for the size
1529 const char *text_start = text;
1531 ft2_font_t *ft2 = fnt->ft2;
1532 qboolean snap = true;
1536 const float *width_of;
1539 tw = R_TextureWidth(fnt->tex);
1540 th = R_TextureHeight(fnt->tex);
1548 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1549 w *= fnt->settings.scale;
1550 h *= fnt->settings.scale;
1555 map_index = Font_IndexForSize(ft2, h, &w, &h);
1557 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1558 fontmap = Font_MapForIndex(ft2, map_index);
1564 // draw the font at its baseline when using freetype
1566 ftbase_y = dh * (4.5/6.0);
1571 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1572 if(!r_draw2d.integer && !r_draw2d_force)
1573 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1575 // R_Mesh_ResetTextureState();
1577 R_Mesh_TexBind(0, fnt->tex);
1578 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1585 //ftbase_x = snap_to_pixel_x(ftbase_x);
1588 startx = snap_to_pixel_x(startx, 0.4);
1589 starty = snap_to_pixel_y(starty, 0.4);
1590 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1593 pix_x = vid.width / vid_conwidth.value;
1594 pix_y = vid.height / vid_conheight.value;
1597 width_of = fontmap->width_of;
1599 width_of = fnt->width_of;
1601 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1606 if (!outcolor || *outcolor == -1)
1607 colorindex = STRING_COLOR_DEFAULT;
1609 colorindex = *outcolor;
1611 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1618 x += r_textshadow.value * vid.width / vid_conwidth.value;
1619 y += r_textshadow.value * vid.height / vid_conheight.value;
1622 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1624 nextch = ch = u8_getnchar(text, &text, bytes_left);
1625 i = text - text_start;
1628 if (ch == ' ' && !fontmap)
1630 x += width_of[(int) ' '] * dw;
1633 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1635 ch = *text; // colors are ascii, so no u8_ needed
1636 if (ch <= '9' && ch >= '0') // ^[0-9] found
1638 colorindex = ch - '0';
1639 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1644 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1646 // building colorindex...
1647 ch = tolower(text[1]);
1648 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1649 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1650 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1651 else tempcolorindex = 0;
1654 ch = tolower(text[2]);
1655 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1656 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1657 else tempcolorindex = 0;
1660 ch = tolower(text[3]);
1661 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1662 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1663 else tempcolorindex = 0;
1666 colorindex = tempcolorindex | 0xf;
1667 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1668 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1669 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1677 else if (ch == STRING_COLOR_TAG)
1686 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1687 // this way we don't need to rebind fnt->tex for every old-style character
1688 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1691 x += 1.0/pix_x * r_textshadow.value;
1692 y += 1.0/pix_y * r_textshadow.value;
1694 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1702 if (map != ft2_oldstyle_map)
1706 // switching from freetype to non-freetype rendering
1707 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1708 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1714 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1715 map = ft2_oldstyle_map;
1719 //num = (unsigned char) text[i];
1720 //thisw = fnt->width_of[num];
1721 thisw = fnt->width_of[ch];
1722 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1723 s = (ch & 15)*0.0625f + (0.5f / tw);
1724 t = (ch >> 4)*0.0625f + (0.5f / th);
1725 u = 0.0625f * thisw - (1.0f / tw);
1726 v = 0.0625f - (1.0f / th);
1727 ac[ 0] = DrawQ_Color[0];ac[ 1] = DrawQ_Color[1];ac[ 2] = DrawQ_Color[2];ac[ 3] = DrawQ_Color[3];
1728 ac[ 4] = DrawQ_Color[0];ac[ 5] = DrawQ_Color[1];ac[ 6] = DrawQ_Color[2];ac[ 7] = DrawQ_Color[3];
1729 ac[ 8] = DrawQ_Color[0];ac[ 9] = DrawQ_Color[1];ac[10] = DrawQ_Color[2];ac[11] = DrawQ_Color[3];
1730 ac[12] = DrawQ_Color[0];ac[13] = DrawQ_Color[1];ac[14] = DrawQ_Color[2];ac[15] = DrawQ_Color[3];
1731 at[ 0] = s ; at[ 1] = t ;
1732 at[ 2] = s+u ; at[ 3] = t ;
1733 at[ 4] = s+u ; at[ 5] = t+v ;
1734 at[ 6] = s ; at[ 7] = t+v ;
1735 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1736 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1737 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1738 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1743 if (batchcount >= QUADELEMENTS_MAXQUADS)
1745 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1746 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1752 x += width_of[ch] * dw;
1754 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1756 // new charmap - need to render
1759 // we need a different character map, render what we currently have:
1760 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1761 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1768 map = FontMap_FindForChar(fontmap, ch);
1771 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1778 // this shouldn't happen
1783 R_SetupShader_Generic(map->pic->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1786 mapch = ch - map->start;
1787 thisw = map->glyphs[mapch].advance_x;
1791 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1798 ac[ 0] = DrawQ_Color[0]; ac[ 1] = DrawQ_Color[1]; ac[ 2] = DrawQ_Color[2]; ac[ 3] = DrawQ_Color[3];
1799 ac[ 4] = DrawQ_Color[0]; ac[ 5] = DrawQ_Color[1]; ac[ 6] = DrawQ_Color[2]; ac[ 7] = DrawQ_Color[3];
1800 ac[ 8] = DrawQ_Color[0]; ac[ 9] = DrawQ_Color[1]; ac[10] = DrawQ_Color[2]; ac[11] = DrawQ_Color[3];
1801 ac[12] = DrawQ_Color[0]; ac[13] = DrawQ_Color[1]; ac[14] = DrawQ_Color[2]; ac[15] = DrawQ_Color[3];
1802 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1803 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1804 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1805 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1806 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1807 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1808 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1809 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1818 if (batchcount >= QUADELEMENTS_MAXQUADS)
1820 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1821 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1834 x -= 1.0/pix_x * r_textshadow.value;
1835 y -= 1.0/pix_y * r_textshadow.value;
1841 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1842 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1846 *outcolor = colorindex;
1848 // note: this relies on the proper text (not shadow) being drawn last
1852 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)
1854 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1857 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)
1859 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1862 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1864 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1867 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1869 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1874 // no ^xrgb management
1875 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1877 int color, numchars = 0;
1878 char *outputend2c = output2c + maxoutchars - 2;
1879 if (!outcolor || *outcolor == -1)
1880 color = STRING_COLOR_DEFAULT;
1884 maxreadchars = 1<<30;
1885 textend = text + maxreadchars;
1886 while (text != textend && *text)
1888 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1890 if (text[1] == STRING_COLOR_TAG)
1892 else if (text[1] >= '0' && text[1] <= '9')
1894 color = text[1] - '0';
1899 if (output2c >= outputend2c)
1901 *output2c++ = *text++;
1902 *output2c++ = color;
1905 output2c[0] = output2c[1] = 0;
1912 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)
1916 _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1917 if(!r_draw2d.integer && !r_draw2d_force)
1920 // R_Mesh_ResetTextureState();
1926 height = pic->height;
1927 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1930 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1932 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1933 floats[0] = floats[9] = x;
1934 floats[1] = floats[4] = y;
1935 floats[3] = floats[6] = x + width;
1936 floats[7] = floats[10] = y + height;
1937 floats[12] = s1;floats[13] = t1;
1938 floats[14] = s2;floats[15] = t2;
1939 floats[16] = s4;floats[17] = t4;
1940 floats[18] = s3;floats[19] = t3;
1941 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1942 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1943 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1944 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1946 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1947 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1950 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1954 if(!r_draw2d.integer && !r_draw2d_force)
1956 DrawQ_ProcessDrawFlag(flags, hasalpha);
1958 // R_Mesh_ResetTextureState();
1959 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1961 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1962 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1965 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1969 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1970 if(!r_draw2d.integer && !r_draw2d_force)
1974 switch(vid.renderpath)
1976 case RENDERPATH_GL11:
1977 case RENDERPATH_GL13:
1978 case RENDERPATH_GL20:
1981 qglBegin(GL_LINE_LOOP);
1982 for (num = 0;num < mesh->num_vertices;num++)
1984 if (mesh->data_color4f)
1985 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]);
1986 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1992 case RENDERPATH_D3D9:
1993 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1995 case RENDERPATH_D3D10:
1996 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1998 case RENDERPATH_D3D11:
1999 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2001 case RENDERPATH_SOFT:
2002 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2004 case RENDERPATH_GLES1:
2005 case RENDERPATH_GLES2:
2006 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2011 //[515]: this is old, delete
2012 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
2014 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
2015 if(!r_draw2d.integer && !r_draw2d_force)
2018 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
2020 switch(vid.renderpath)
2022 case RENDERPATH_GL11:
2023 case RENDERPATH_GL13:
2024 case RENDERPATH_GL20:
2028 //qglLineWidth(width);CHECKGLERROR
2030 GL_Color(r,g,b,alpha);
2033 qglVertex2f(x1, y1);
2034 qglVertex2f(x2, y2);
2039 case RENDERPATH_D3D9:
2040 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2042 case RENDERPATH_D3D10:
2043 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2045 case RENDERPATH_D3D11:
2046 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2048 case RENDERPATH_SOFT:
2049 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2051 case RENDERPATH_GLES1:
2052 case RENDERPATH_GLES2:
2053 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2058 void DrawQ_Lines (float width, int numlines, const float *vertex3f, const float *color4f, int flags)
2061 qboolean hasalpha = false;
2062 for (i = 0;i < numlines*2;i++)
2063 if (color4f[i*4+3] < 1.0f)
2066 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, hasalpha ? 0.5f : 1.0f);
2068 if(!r_draw2d.integer && !r_draw2d_force)
2071 switch(vid.renderpath)
2073 case RENDERPATH_GL11:
2074 case RENDERPATH_GL13:
2075 case RENDERPATH_GL20:
2078 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
2080 //qglLineWidth(width);CHECKGLERROR
2083 R_Mesh_PrepareVertices_Generic_Arrays(numlines*2, vertex3f, color4f, NULL);
2084 qglDrawArrays(GL_LINES, 0, numlines*2);
2087 case RENDERPATH_D3D9:
2088 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2090 case RENDERPATH_D3D10:
2091 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2093 case RENDERPATH_D3D11:
2094 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2096 case RENDERPATH_SOFT:
2097 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2099 case RENDERPATH_GLES1:
2100 case RENDERPATH_GLES2:
2101 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2106 void DrawQ_SetClipArea(float x, float y, float width, float height)
2111 // We have to convert the con coords into real coords
2112 // OGL uses top to bottom
2113 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
2114 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
2115 iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
2116 ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
2117 switch(vid.renderpath)
2119 case RENDERPATH_GL11:
2120 case RENDERPATH_GL13:
2121 case RENDERPATH_GL20:
2122 case RENDERPATH_GLES1:
2123 case RENDERPATH_GLES2:
2124 case RENDERPATH_SOFT:
2125 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
2127 case RENDERPATH_D3D9:
2128 GL_Scissor(ix, iy, iw, ih);
2130 case RENDERPATH_D3D10:
2131 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2133 case RENDERPATH_D3D11:
2134 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2138 GL_ScissorTest(true);
2141 void DrawQ_ResetClipArea(void)
2144 GL_ScissorTest(false);
2147 void DrawQ_Finish(void)
2149 r_refdef.draw2dstage = 0;
2152 void DrawQ_RecalcView(void)
2154 if(r_refdef.draw2dstage)
2155 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2158 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2159 void R_DrawGamma(void)
2162 switch(vid.renderpath)
2164 case RENDERPATH_GL20:
2165 case RENDERPATH_D3D9:
2166 case RENDERPATH_D3D10:
2167 case RENDERPATH_D3D11:
2168 case RENDERPATH_GLES2:
2169 if (vid_usinghwgamma || v_glslgamma.integer)
2172 case RENDERPATH_GL11:
2173 case RENDERPATH_GL13:
2174 if (vid_usinghwgamma)
2177 case RENDERPATH_GLES1:
2178 case RENDERPATH_SOFT:
2181 // all the blends ignore depth
2182 // R_Mesh_ResetTextureState();
2183 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, true, true);
2185 GL_DepthRange(0, 1);
2186 GL_PolygonOffset(0, 0);
2187 GL_DepthTest(false);
2189 // interpretation of brightness and contrast:
2190 // color range := brightness .. (brightness + contrast)
2191 // i.e. "c *= contrast; c += brightness"
2192 // plausible values for brightness thus range from -contrast to 1
2194 // apply pre-brightness (subtractive brightness, for where contrast was >= 1)
2195 if (vid.support.ext_blend_subtract)
2197 if (v_color_enable.integer)
2199 c[0] = -v_color_black_r.value / v_color_white_r.value;
2200 c[1] = -v_color_black_g.value / v_color_white_g.value;
2201 c[2] = -v_color_black_b.value / v_color_white_b.value;
2204 c[0] = c[1] = c[2] = -v_brightness.value / v_contrast.value;
2205 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2207 // need SUBTRACTIVE blending to do this!
2208 GL_BlendEquationSubtract(true);
2209 GL_BlendFunc(GL_ONE, GL_ONE);
2210 GL_Color(c[0], c[1], c[2], 1);
2211 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2212 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2213 GL_BlendEquationSubtract(false);
2218 if (v_color_enable.integer)
2220 c[0] = v_color_white_r.value;
2221 c[1] = v_color_white_g.value;
2222 c[2] = v_color_white_b.value;
2225 c[0] = c[1] = c[2] = v_contrast.value;
2226 if (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2228 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2229 while (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2232 cc[0] = bound(0, c[0] - 1, 1);
2233 cc[1] = bound(0, c[1] - 1, 1);
2234 cc[2] = bound(0, c[2] - 1, 1);
2235 GL_Color(cc[0], cc[1], cc[2], 1);
2236 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2237 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2243 if (c[0] <= 0.997f || c[1] <= 0.997f || c[2] <= 0.997f)
2245 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
2246 GL_Color(c[0], c[1], c[2], 1);
2247 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2248 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2251 // apply post-brightness (additive brightness, for where contrast was <= 1)
2252 if (v_color_enable.integer)
2254 c[0] = v_color_black_r.value;
2255 c[1] = v_color_black_g.value;
2256 c[2] = v_color_black_b.value;
2259 c[0] = c[1] = c[2] = v_brightness.value;
2260 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2262 GL_BlendFunc(GL_ONE, GL_ONE);
2263 GL_Color(c[0], c[1], c[2], 1);
2264 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2265 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);