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_NOCOMPRESSION) && gl_texturecompression_2d.integer && gl_texturecompression.integer)
332 texflags |= TEXF_COMPRESS;
334 // check whether the picture has already been cached
335 crc = CRC_Block((unsigned char *)path, strlen(path));
336 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
337 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
339 if (!strcmp (path, pic->name))
341 // if it was created (or replaced) by Draw_NewPic, just return it
342 if(pic->flags & CACHEPICFLAG_NEWPIC)
344 if (!((pic->texflags ^ texflags) & ~(TEXF_COMPRESS))) // ignore TEXF_COMPRESS when comparing, because fallback pics remove the flag
346 if(!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
349 pic->autoload = false; // persist it
351 goto reload; // load it below, and then persist
358 if (numcachepics == MAX_CACHED_PICS)
360 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
361 // FIXME: support NULL in callers?
362 return cachepics; // return the first one
364 pic = cachepics + (numcachepics++);
365 memset(pic, 0, sizeof(*pic));
366 strlcpy (pic->name, path, sizeof(pic->name));
368 pic->chain = cachepichash[hashkey];
369 cachepichash[hashkey] = pic;
372 // TODO why does this crash?
373 if(pic->allow_free_tex && pic->tex)
374 R_PurgeTexture(pic->tex);
376 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
377 pic->flags = cachepicflags;
378 pic->tex = CL_GetDynTexture( path );
379 // if so, set the width/height, too
381 pic->allow_free_tex = false;
382 pic->width = R_TextureWidth(pic->tex);
383 pic->height = R_TextureHeight(pic->tex);
384 // we're done now (early-out)
388 pic->allow_free_tex = true;
390 pic->hasalpha = true; // assume alpha unless we know it has none
391 pic->texflags = texflags;
392 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
393 pic->lastusedframe = draw_frame;
395 // load a high quality image from disk if possible
396 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)))
398 // note this loads even if autoload is true, otherwise we can't get the width/height
400 pic->hasalpha = ddshasalpha;
401 pic->width = R_TextureWidth(pic->tex);
402 pic->height = R_TextureHeight(pic->tex);
404 if (!loaded && ((pixels = loadimagepixelsbgra(pic->name, false, true, false, NULL)) || (!strncmp(pic->name, "gfx/", 4) && (pixels = loadimagepixelsbgra(pic->name+4, false, true, false, NULL)))))
407 pic->hasalpha = false;
408 if (pic->texflags & TEXF_ALPHA)
410 for (j = 3;j < image_width * image_height * 4;j += 4)
414 pic->hasalpha = true;
420 pic->width = image_width;
421 pic->height = image_height;
424 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);
426 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
427 R_SaveTextureDDSFile(pic->tex, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
433 pic->autoload = false;
434 // never compress the fallback images
435 pic->texflags &= ~TEXF_COMPRESS;
438 // now read the low quality version (wad or lmp file), and take the pic
439 // size from that even if we don't upload the texture, this way the pics
440 // show up the right size in the menu even if they were replaced with
441 // higher or lower resolution versions
442 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", pic->name);
443 if (!strncmp(pic->name, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
445 if (developer_loading.integer)
446 Con_Printf("loading lump \"%s\"\n", pic->name);
450 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
451 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
452 // if no high quality replacement image was found, upload the original low quality texture
456 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);
461 else if ((lmpdata = W_GetLumpName (pic->name + 4)))
463 if (developer_loading.integer)
464 Con_Printf("loading gfx.wad lump \"%s\"\n", pic->name + 4);
466 if (!strcmp(pic->name, "gfx/conchars"))
468 // conchars is a raw image and with color 0 as transparent instead of 255
471 // if no high quality replacement image was found, upload the original low quality texture
475 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, 128, 128, lmpdata, vid.sRGB2D != 0 ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
480 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
481 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
482 // if no high quality replacement image was found, upload the original low quality texture
486 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);
498 // if it's not found on disk, generate an image
499 pic->tex = draw_generatepic(pic->name, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
500 pic->width = R_TextureWidth(pic->tex);
501 pic->height = R_TextureHeight(pic->tex);
502 pic->allow_free_tex = (pic->tex != r_texture_notexture);
508 cachepic_t *Draw_CachePic (const char *path)
510 return Draw_CachePic_Flags (path, 0); // default to persistent!
513 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
516 if (pic->autoload && !pic->tex)
518 if (pic->tex == NULL && r_texture_dds_load.integer != 0)
520 qboolean ddshasalpha;
521 float ddsavgcolor[4];
522 pic->tex = R_LoadTextureDDSFile(drawtexturepool, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), vid.sRGB2D, pic->texflags, &ddshasalpha, ddsavgcolor, 0);
524 if (pic->tex == NULL)
526 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, vid.sRGB2D);
528 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
529 R_SaveTextureDDSFile(pic->tex, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
532 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
534 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, vid.sRGB2D);
536 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
537 R_SaveTextureDDSFile(pic->tex, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
540 if (pic->tex == NULL)
541 pic->tex = draw_generatepic(pic->name, true);
543 pic->lastusedframe = draw_frame;
547 void Draw_Frame(void)
551 static double nextpurgetime;
552 if (nextpurgetime > realtime)
554 nextpurgetime = realtime + 0.05;
555 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
557 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
559 R_FreeTexture(pic->tex);
566 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
571 crc = CRC_Block((unsigned char *)picname, strlen(picname));
572 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
573 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
574 if (!strcmp (picname, pic->name))
579 if (pic->flags == CACHEPICFLAG_NEWPIC && pic->tex && pic->width == width && pic->height == height)
581 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, 0, width, height, 1);
587 if (numcachepics == MAX_CACHED_PICS)
589 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
590 // FIXME: support NULL in callers?
591 return cachepics; // return the first one
593 pic = cachepics + (numcachepics++);
594 memset(pic, 0, sizeof(*pic));
595 strlcpy (pic->name, picname, sizeof(pic->name));
597 pic->chain = cachepichash[hashkey];
598 cachepichash[hashkey] = pic;
601 pic->flags = CACHEPICFLAG_NEWPIC; // disable texflags checks in Draw_CachePic
603 pic->height = height;
604 if (pic->allow_free_tex && pic->tex)
605 R_FreeTexture(pic->tex);
606 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0), -1, NULL);
610 void Draw_FreePic(const char *picname)
615 // this doesn't really free the pic, but does free it's texture
616 crc = CRC_Block((unsigned char *)picname, strlen(picname));
617 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
618 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
620 if (!strcmp (picname, pic->name) && pic->tex)
622 R_FreeTexture(pic->tex);
631 static float snap_to_pixel_x(float x, float roundUpAt);
632 extern int con_linewidth; // to force rewrapping
633 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
637 char widthfile[MAX_QPATH];
639 fs_offset_t widthbufsize;
641 if(override || !fnt->texpath[0])
643 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
644 // load the cvars when the font is FIRST loader
645 fnt->settings.scale = scale;
646 fnt->settings.voffset = voffset;
647 fnt->settings.antialias = r_font_antialias.integer;
648 fnt->settings.hinting = r_font_hinting.integer;
649 fnt->settings.outline = r_font_postprocess_outline.value;
650 fnt->settings.blur = r_font_postprocess_blur.value;
651 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
652 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
653 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
656 if (fnt->settings.scale <= 0)
657 fnt->settings.scale = 1;
659 if(drawtexturepool == NULL)
660 return; // before gl_draw_start, so will be loaded later
664 // clear freetype font
665 Font_UnloadFont(fnt->ft2);
670 if(fnt->req_face != -1)
672 if(!Font_LoadFont(fnt->texpath, fnt))
673 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
676 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
677 if(fnt->tex == r_texture_notexture)
679 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
681 if (!fnt->fallbacks[i][0])
683 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
684 if(fnt->tex != r_texture_notexture)
687 if(fnt->tex == r_texture_notexture)
689 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
690 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
693 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
696 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
698 // unspecified width == 1 (base width)
699 for(ch = 0; ch < 256; ++ch)
700 fnt->width_of[ch] = 1;
702 // FIXME load "name.width", if it fails, fill all with 1
703 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
705 float extraspacing = 0;
706 const char *p = widthbuf;
711 if(!COM_ParseToken_Simple(&p, false, false, true))
729 fnt->width_of[ch] = atof(com_token) + extraspacing;
733 if(!strcmp(com_token, "extraspacing"))
735 if(!COM_ParseToken_Simple(&p, false, false, true))
737 extraspacing = atof(com_token);
739 else if(!strcmp(com_token, "scale"))
741 if(!COM_ParseToken_Simple(&p, false, false, true))
743 fnt->settings.scale = atof(com_token);
747 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
748 if(!COM_ParseToken_Simple(&p, false, false, true))
760 for (i = 0; i < MAX_FONT_SIZES; ++i)
762 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
765 for(ch = 0; ch < 256; ++ch)
766 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
770 maxwidth = fnt->width_of[0];
771 for(i = 1; i < 256; ++i)
772 maxwidth = max(maxwidth, fnt->width_of[i]);
773 fnt->maxwidth = maxwidth;
775 // fix up maxwidth for overlap
776 fnt->maxwidth *= fnt->settings.scale;
778 if(fnt == FONT_CONSOLE)
779 con_linewidth = -1; // rewrap console in next frame
782 extern cvar_t developer_font;
783 dp_font_t *FindFont(const char *title, qboolean allocate_new)
788 for(i = 0; i < dp_fonts.maxsize; ++i)
789 if(!strcmp(dp_fonts.f[i].title, title))
790 return &dp_fonts.f[i];
791 // if not found - try allocate
794 // find any font with empty title
795 for(i = 0; i < dp_fonts.maxsize; ++i)
797 if(!strcmp(dp_fonts.f[i].title, ""))
799 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
800 return &dp_fonts.f[i];
803 // if no any 'free' fonts - expand buffer
804 oldsize = dp_fonts.maxsize;
805 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
806 if (developer_font.integer)
807 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", oldsize, dp_fonts.maxsize);
808 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
809 // relink ft2 structures
810 for(i = 0; i < oldsize; ++i)
811 if (dp_fonts.f[i].ft2)
812 dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings;
813 // register a font in first expanded slot
814 strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
815 return &dp_fonts.f[oldsize];
820 static float snap_to_pixel_x(float x, float roundUpAt)
822 float pixelpos = x * vid.width / vid_conwidth.value;
823 int snap = (int) pixelpos;
824 if (pixelpos - snap >= roundUpAt) ++snap;
825 return ((float)snap * vid_conwidth.value / vid.width);
827 x = (int)(x * vid.width / vid_conwidth.value);
828 x = (x * vid_conwidth.value / vid.width);
833 static float snap_to_pixel_y(float y, float roundUpAt)
835 float pixelpos = y * vid.height / vid_conheight.value;
836 int snap = (int) pixelpos;
837 if (pixelpos - snap > roundUpAt) ++snap;
838 return ((float)snap * vid_conheight.value / vid.height);
840 y = (int)(y * vid.height / vid_conheight.value);
841 y = (y * vid_conheight.value / vid.height);
846 static void LoadFont_f(void)
850 const char *filelist, *c, *cm;
851 float sz, scale, voffset;
852 char mainfont[MAX_QPATH];
856 Con_Printf("Available font commands:\n");
857 for(i = 0; i < dp_fonts.maxsize; ++i)
858 if (dp_fonts.f[i].title[0])
859 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
860 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
861 "can specify multiple fonts and faces\n"
862 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
863 "to load face 2 of the font gfx/vera-sans and use face 1\n"
864 "of gfx/fallback as fallback font.\n"
865 "You can also specify a list of font sizes to load, like this:\n"
866 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
867 "In many cases, 8 12 16 24 32 should be a good choice.\n"
869 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
870 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
874 f = FindFont(Cmd_Argv(1), true);
877 Con_Printf("font function not found\n");
882 filelist = "gfx/conchars";
884 filelist = Cmd_Argv(2);
886 memset(f->fallbacks, 0, sizeof(f->fallbacks));
887 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
889 // first font is handled "normally"
890 c = strchr(filelist, ':');
891 cm = strchr(filelist, ',');
892 if(c && (!cm || c < cm))
893 f->req_face = atoi(c+1);
900 if(!c || (c - filelist) > MAX_QPATH)
901 strlcpy(mainfont, filelist, sizeof(mainfont));
904 memcpy(mainfont, filelist, c - filelist);
905 mainfont[c - filelist] = 0;
908 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
910 c = strchr(filelist, ',');
916 c = strchr(filelist, ':');
917 cm = strchr(filelist, ',');
918 if(c && (!cm || c < cm))
919 f->fallback_faces[i] = atoi(c+1);
922 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
925 if(!c || (c-filelist) > MAX_QPATH)
927 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
931 memcpy(f->fallbacks[i], filelist, c - filelist);
932 f->fallbacks[i][c - filelist] = 0;
936 // for now: by default load only one size: the default size
938 for(i = 1; i < MAX_FONT_SIZES; ++i)
939 f->req_sizes[i] = -1;
945 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
948 if (!strcmp(Cmd_Argv(i), "scale"))
952 scale = atof(Cmd_Argv(i));
955 if (!strcmp(Cmd_Argv(i), "voffset"))
959 voffset = atof(Cmd_Argv(i));
964 continue; // no slot for other sizes
966 // parse one of sizes
967 sz = atof(Cmd_Argv(i));
968 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
970 // search for duplicated sizes
972 for (j=0; j<sizes; j++)
973 if (f->req_sizes[j] == sz)
976 continue; // sz already in req_sizes, don't add it again
978 if (sizes == MAX_FONT_SIZES)
980 Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
984 f->req_sizes[sizes] = sz;
990 LoadFont(true, mainfont, f, scale, voffset);
998 static void gl_draw_start(void)
1002 drawtexturepool = R_AllocTexturePool();
1005 memset(cachepichash, 0, sizeof(cachepichash));
1009 // load default font textures
1010 for(i = 0; i < dp_fonts.maxsize; ++i)
1011 if (dp_fonts.f[i].title[0])
1012 LoadFont(false, va(vabuf, sizeof(vabuf), "gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
1014 // draw the loading screen so people have something to see in the newly opened window
1015 SCR_UpdateLoadingScreen(true);
1018 static void gl_draw_shutdown(void)
1022 R_FreeTexturePool(&drawtexturepool);
1025 memset(cachepichash, 0, sizeof(cachepichash));
1028 static void gl_draw_newmap(void)
1033 void GL_Draw_Init (void)
1037 Cvar_RegisterVariable(&r_font_postprocess_blur);
1038 Cvar_RegisterVariable(&r_font_postprocess_outline);
1039 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
1040 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
1041 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
1042 Cvar_RegisterVariable(&r_font_hinting);
1043 Cvar_RegisterVariable(&r_font_antialias);
1044 Cvar_RegisterVariable(&r_textshadow);
1045 Cvar_RegisterVariable(&r_textbrightness);
1046 Cvar_RegisterVariable(&r_textcontrast);
1048 // allocate fonts storage
1049 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
1050 dp_fonts.maxsize = MAX_FONTS;
1051 dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
1052 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
1054 // assign starting font names
1055 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
1056 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
1057 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
1058 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
1059 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
1060 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
1061 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
1062 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
1063 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
1064 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
1065 if(!FONT_USER(i)->title[0])
1066 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
1068 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
1069 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
1072 static void _DrawQ_Setup(void) // see R_ResetViewRendering2D
1074 r_viewport_t viewport;
1075 if (r_refdef.draw2dstage == 1)
1077 r_refdef.draw2dstage = 1;
1080 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);
1081 R_Mesh_SetRenderTargets(0, NULL, NULL, NULL, NULL, NULL);
1082 R_SetViewport(&viewport);
1083 //GL_Scissor(viewport.x, viewport.y, viewport.width, viewport.height); // DrawQ_SetClipArea would do this
1084 GL_Color(1, 1, 1, 1);
1085 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
1086 //GL_BlendFunc(GL_ONE, GL_ZERO); // DrawQ_ProcessDrawFlag does this
1087 GL_ScissorTest(false);
1088 GL_DepthMask(false);
1089 GL_DepthRange(0, 1);
1090 GL_DepthTest(false);
1091 GL_DepthFunc(GL_LEQUAL);
1092 R_EntityMatrix(&identitymatrix);
1093 R_Mesh_ResetTextureState();
1094 GL_PolygonOffset(0, 0);
1095 //R_SetStencil(false, 255, GL_KEEP, GL_KEEP, GL_KEEP, GL_ALWAYS, 128, 255); // not needed
1096 //qglEnable(GL_POLYGON_OFFSET_FILL); // we never use polygon offset here
1097 GL_CullFace(GL_NONE);
1100 qboolean r_draw2d_force = false;
1101 static void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1105 if(!r_draw2d.integer && !r_draw2d_force)
1107 DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1109 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1111 if(flags == DRAWFLAG_ADDITIVE)
1113 GL_DepthMask(false);
1114 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1116 else if(flags == DRAWFLAG_MODULATE)
1118 GL_DepthMask(false);
1119 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1121 else if(flags == DRAWFLAG_2XMODULATE)
1123 GL_DepthMask(false);
1124 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1126 else if(flags == DRAWFLAG_SCREEN)
1128 GL_DepthMask(false);
1129 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1133 GL_DepthMask(false);
1134 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1139 GL_BlendFunc(GL_ONE, GL_ZERO);
1143 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1147 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1148 if(!r_draw2d.integer && !r_draw2d_force)
1151 // R_Mesh_ResetTextureState();
1152 floats[12] = 0.0f;floats[13] = 0.0f;
1153 floats[14] = 1.0f;floats[15] = 0.0f;
1154 floats[16] = 1.0f;floats[17] = 1.0f;
1155 floats[18] = 0.0f;floats[19] = 1.0f;
1156 floats[20] = floats[24] = floats[28] = floats[32] = red;
1157 floats[21] = floats[25] = floats[29] = floats[33] = green;
1158 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1159 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1165 height = pic->height;
1166 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1169 // AK07: lets be texel correct on the corners
1171 float horz_offset = 0.5f / pic->width;
1172 float vert_offset = 0.5f / pic->height;
1174 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1175 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1176 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1177 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1182 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1184 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1185 floats[0] = floats[9] = x;
1186 floats[1] = floats[4] = y;
1187 floats[3] = floats[6] = x + width;
1188 floats[7] = floats[10] = y + height;
1190 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1191 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1194 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)
1197 float af = DEG2RAD(-angle); // forward
1198 float ar = DEG2RAD(-angle + 90); // right
1199 float sinaf = sin(af);
1200 float cosaf = cos(af);
1201 float sinar = sin(ar);
1202 float cosar = cos(ar);
1204 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1205 if(!r_draw2d.integer && !r_draw2d_force)
1208 // R_Mesh_ResetTextureState();
1214 height = pic->height;
1215 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1218 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1220 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1223 floats[0] = x - cosaf*org_x - cosar*org_y;
1224 floats[1] = y - sinaf*org_x - sinar*org_y;
1227 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1228 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1231 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1232 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1235 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1236 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1238 floats[12] = 0.0f;floats[13] = 0.0f;
1239 floats[14] = 1.0f;floats[15] = 0.0f;
1240 floats[16] = 1.0f;floats[17] = 1.0f;
1241 floats[18] = 0.0f;floats[19] = 1.0f;
1242 floats[20] = floats[24] = floats[28] = floats[32] = red;
1243 floats[21] = floats[25] = floats[29] = floats[33] = green;
1244 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1245 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1247 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1248 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1251 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1255 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1256 if(!r_draw2d.integer && !r_draw2d_force)
1259 // R_Mesh_ResetTextureState();
1260 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1262 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1263 floats[0] = floats[9] = x;
1264 floats[1] = floats[4] = y;
1265 floats[3] = floats[6] = x + width;
1266 floats[7] = floats[10] = y + height;
1267 floats[12] = 0.0f;floats[13] = 0.0f;
1268 floats[14] = 1.0f;floats[15] = 0.0f;
1269 floats[16] = 1.0f;floats[17] = 1.0f;
1270 floats[18] = 0.0f;floats[19] = 1.0f;
1271 floats[20] = floats[24] = floats[28] = floats[32] = red;
1272 floats[21] = floats[25] = floats[29] = floats[33] = green;
1273 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1274 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1276 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1277 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1280 /// color tag printing
1281 static const vec4_t string_colors[] =
1284 // LordHavoc: why on earth is cyan before magenta in Quake3?
1285 // LordHavoc: note: Doom3 uses white for [0] and [7]
1286 {0.0, 0.0, 0.0, 1.0}, // black
1287 {1.0, 0.0, 0.0, 1.0}, // red
1288 {0.0, 1.0, 0.0, 1.0}, // green
1289 {1.0, 1.0, 0.0, 1.0}, // yellow
1290 {0.0, 0.0, 1.0, 1.0}, // blue
1291 {0.0, 1.0, 1.0, 1.0}, // cyan
1292 {1.0, 0.0, 1.0, 1.0}, // magenta
1293 {1.0, 1.0, 1.0, 1.0}, // white
1294 // [515]'s BX_COLOREDTEXT extension
1295 {1.0, 1.0, 1.0, 0.5}, // half transparent
1296 {0.5, 0.5, 0.5, 1.0} // half brightness
1297 // Black's color table
1298 //{1.0, 1.0, 1.0, 1.0},
1299 //{1.0, 0.0, 0.0, 1.0},
1300 //{0.0, 1.0, 0.0, 1.0},
1301 //{0.0, 0.0, 1.0, 1.0},
1302 //{1.0, 1.0, 0.0, 1.0},
1303 //{0.0, 1.0, 1.0, 1.0},
1304 //{1.0, 0.0, 1.0, 1.0},
1305 //{0.1, 0.1, 0.1, 1.0}
1308 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1310 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1312 float C = r_textcontrast.value;
1313 float B = r_textbrightness.value;
1314 if (colorindex & 0x10000) // that bit means RGB color
1316 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1317 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1318 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1319 color[3] = (colorindex & 0xf) / 15.0;
1322 Vector4Copy(string_colors[colorindex], color);
1323 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1326 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1327 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1331 // NOTE: this function always draws exactly one character if maxwidth <= 0
1332 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)
1334 const char *text_start = text;
1335 int colorindex = STRING_COLOR_DEFAULT;
1338 Uchar ch, mapch, nextch;
1339 Uchar prevch = 0; // used for kerning
1344 ft2_font_map_t *fontmap = NULL;
1345 ft2_font_map_t *map = NULL;
1346 //ft2_font_map_t *prevmap = NULL;
1347 ft2_font_t *ft2 = fnt->ft2;
1349 qboolean snap = true;
1350 qboolean least_one = false;
1351 float dw; // display w
1352 //float dh; // display h
1353 const float *width_of;
1360 // do this in the end
1361 w *= fnt->settings.scale;
1362 h *= fnt->settings.scale;
1364 // find the most fitting size:
1368 map_index = Font_IndexForSize(ft2, h, &w, &h);
1370 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1371 fontmap = Font_MapForIndex(ft2, map_index);
1380 if (!outcolor || *outcolor == -1)
1381 colorindex = STRING_COLOR_DEFAULT;
1383 colorindex = *outcolor;
1385 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1386 // ftbase_x = snap_to_pixel_x(0);
1391 maxwidth = -maxwidth;
1395 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1398 width_of = fontmap->width_of;
1400 width_of = fnt->width_of;
1402 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1405 nextch = ch = u8_getnchar(text, &text, bytes_left);
1406 i = text - text_start;
1409 if (ch == ' ' && !fontmap)
1411 if(!least_one || i0) // never skip the first character
1412 if(x + width_of[(int) ' '] * dw > maxwidth)
1415 break; // oops, can't draw this
1417 x += width_of[(int) ' '] * dw;
1420 // i points to the char after ^
1421 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1423 ch = *text; // colors are ascii, so no u8_ needed
1424 if (ch <= '9' && ch >= '0') // ^[0-9] found
1426 colorindex = ch - '0';
1431 // i points to the char after ^...
1432 // i+3 points to 3 in ^x123
1433 // i+3 == *maxlen would mean that char is missing
1434 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1436 // building colorindex...
1437 ch = tolower(text[1]);
1438 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1439 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1440 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1441 else tempcolorindex = 0;
1444 ch = tolower(text[2]);
1445 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1446 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1447 else tempcolorindex = 0;
1450 ch = tolower(text[3]);
1451 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1452 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1453 else tempcolorindex = 0;
1456 colorindex = tempcolorindex | 0xf;
1457 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1465 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1474 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1481 map = ft2_oldstyle_map;
1483 if(!least_one || i0) // never skip the first character
1484 if(x + width_of[ch] * dw > maxwidth)
1487 break; // oops, can't draw this
1489 x += width_of[ch] * dw;
1491 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1493 map = FontMap_FindForChar(fontmap, ch);
1496 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1502 mapch = ch - map->start;
1503 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1505 x += map->glyphs[mapch].advance_x * dw;
1514 *outcolor = colorindex;
1519 float DrawQ_Color[4];
1520 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)
1522 int shadow, colorindex = STRING_COLOR_DEFAULT;
1524 float x = startx, y, s, t, u, v, thisw;
1525 float *av, *at, *ac;
1527 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1528 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1529 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1530 Uchar ch, mapch, nextch;
1531 Uchar prevch = 0; // used for kerning
1534 //ft2_font_map_t *prevmap = NULL; // the previous map
1535 ft2_font_map_t *map = NULL; // the currently used map
1536 ft2_font_map_t *fontmap = NULL; // the font map for the size
1538 const char *text_start = text;
1540 ft2_font_t *ft2 = fnt->ft2;
1541 qboolean snap = true;
1545 const float *width_of;
1548 tw = R_TextureWidth(fnt->tex);
1549 th = R_TextureHeight(fnt->tex);
1557 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1558 w *= fnt->settings.scale;
1559 h *= fnt->settings.scale;
1564 map_index = Font_IndexForSize(ft2, h, &w, &h);
1566 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1567 fontmap = Font_MapForIndex(ft2, map_index);
1573 // draw the font at its baseline when using freetype
1575 ftbase_y = dh * (4.5/6.0);
1580 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1581 if(!r_draw2d.integer && !r_draw2d_force)
1582 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1584 // R_Mesh_ResetTextureState();
1586 R_Mesh_TexBind(0, fnt->tex);
1587 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1594 //ftbase_x = snap_to_pixel_x(ftbase_x);
1597 startx = snap_to_pixel_x(startx, 0.4);
1598 starty = snap_to_pixel_y(starty, 0.4);
1599 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1602 pix_x = vid.width / vid_conwidth.value;
1603 pix_y = vid.height / vid_conheight.value;
1606 width_of = fontmap->width_of;
1608 width_of = fnt->width_of;
1610 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1615 if (!outcolor || *outcolor == -1)
1616 colorindex = STRING_COLOR_DEFAULT;
1618 colorindex = *outcolor;
1620 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1627 x += r_textshadow.value * vid.width / vid_conwidth.value;
1628 y += r_textshadow.value * vid.height / vid_conheight.value;
1631 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1633 nextch = ch = u8_getnchar(text, &text, bytes_left);
1634 i = text - text_start;
1637 if (ch == ' ' && !fontmap)
1639 x += width_of[(int) ' '] * dw;
1642 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1644 ch = *text; // colors are ascii, so no u8_ needed
1645 if (ch <= '9' && ch >= '0') // ^[0-9] found
1647 colorindex = ch - '0';
1648 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1653 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1655 // building colorindex...
1656 ch = tolower(text[1]);
1657 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1658 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1659 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1660 else tempcolorindex = 0;
1663 ch = tolower(text[2]);
1664 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1665 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1666 else tempcolorindex = 0;
1669 ch = tolower(text[3]);
1670 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1671 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1672 else tempcolorindex = 0;
1675 colorindex = tempcolorindex | 0xf;
1676 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1677 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1678 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1686 else if (ch == STRING_COLOR_TAG)
1695 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1696 // this way we don't need to rebind fnt->tex for every old-style character
1697 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1700 x += 1.0/pix_x * r_textshadow.value;
1701 y += 1.0/pix_y * r_textshadow.value;
1703 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1711 if (map != ft2_oldstyle_map)
1715 // switching from freetype to non-freetype rendering
1716 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1717 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1723 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1724 map = ft2_oldstyle_map;
1728 //num = (unsigned char) text[i];
1729 //thisw = fnt->width_of[num];
1730 thisw = fnt->width_of[ch];
1731 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1732 s = (ch & 15)*0.0625f + (0.5f / tw);
1733 t = (ch >> 4)*0.0625f + (0.5f / th);
1734 u = 0.0625f * thisw - (1.0f / tw);
1735 v = 0.0625f - (1.0f / th);
1736 ac[ 0] = DrawQ_Color[0];ac[ 1] = DrawQ_Color[1];ac[ 2] = DrawQ_Color[2];ac[ 3] = DrawQ_Color[3];
1737 ac[ 4] = DrawQ_Color[0];ac[ 5] = DrawQ_Color[1];ac[ 6] = DrawQ_Color[2];ac[ 7] = DrawQ_Color[3];
1738 ac[ 8] = DrawQ_Color[0];ac[ 9] = DrawQ_Color[1];ac[10] = DrawQ_Color[2];ac[11] = DrawQ_Color[3];
1739 ac[12] = DrawQ_Color[0];ac[13] = DrawQ_Color[1];ac[14] = DrawQ_Color[2];ac[15] = DrawQ_Color[3];
1740 at[ 0] = s ; at[ 1] = t ;
1741 at[ 2] = s+u ; at[ 3] = t ;
1742 at[ 4] = s+u ; at[ 5] = t+v ;
1743 at[ 6] = s ; at[ 7] = t+v ;
1744 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1745 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1746 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1747 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1752 if (batchcount >= QUADELEMENTS_MAXQUADS)
1754 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1755 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1761 x += width_of[ch] * dw;
1763 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1765 // new charmap - need to render
1768 // we need a different character map, render what we currently have:
1769 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1770 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1777 map = FontMap_FindForChar(fontmap, ch);
1780 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1787 // this shouldn't happen
1792 R_SetupShader_Generic(map->pic->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1795 mapch = ch - map->start;
1796 thisw = map->glyphs[mapch].advance_x;
1800 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1807 ac[ 0] = DrawQ_Color[0]; ac[ 1] = DrawQ_Color[1]; ac[ 2] = DrawQ_Color[2]; ac[ 3] = DrawQ_Color[3];
1808 ac[ 4] = DrawQ_Color[0]; ac[ 5] = DrawQ_Color[1]; ac[ 6] = DrawQ_Color[2]; ac[ 7] = DrawQ_Color[3];
1809 ac[ 8] = DrawQ_Color[0]; ac[ 9] = DrawQ_Color[1]; ac[10] = DrawQ_Color[2]; ac[11] = DrawQ_Color[3];
1810 ac[12] = DrawQ_Color[0]; ac[13] = DrawQ_Color[1]; ac[14] = DrawQ_Color[2]; ac[15] = DrawQ_Color[3];
1811 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1812 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1813 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1814 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1815 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1816 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1817 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1818 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1827 if (batchcount >= QUADELEMENTS_MAXQUADS)
1829 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1830 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1843 x -= 1.0/pix_x * r_textshadow.value;
1844 y -= 1.0/pix_y * r_textshadow.value;
1850 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1851 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1855 *outcolor = colorindex;
1857 // note: this relies on the proper text (not shadow) being drawn last
1861 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)
1863 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1866 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)
1868 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1871 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1873 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1876 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1878 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1883 // no ^xrgb management
1884 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1886 int color, numchars = 0;
1887 char *outputend2c = output2c + maxoutchars - 2;
1888 if (!outcolor || *outcolor == -1)
1889 color = STRING_COLOR_DEFAULT;
1893 maxreadchars = 1<<30;
1894 textend = text + maxreadchars;
1895 while (text != textend && *text)
1897 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1899 if (text[1] == STRING_COLOR_TAG)
1901 else if (text[1] >= '0' && text[1] <= '9')
1903 color = text[1] - '0';
1908 if (output2c >= outputend2c)
1910 *output2c++ = *text++;
1911 *output2c++ = color;
1914 output2c[0] = output2c[1] = 0;
1921 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)
1925 _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1926 if(!r_draw2d.integer && !r_draw2d_force)
1929 // R_Mesh_ResetTextureState();
1935 height = pic->height;
1936 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1939 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1941 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1942 floats[0] = floats[9] = x;
1943 floats[1] = floats[4] = y;
1944 floats[3] = floats[6] = x + width;
1945 floats[7] = floats[10] = y + height;
1946 floats[12] = s1;floats[13] = t1;
1947 floats[14] = s2;floats[15] = t2;
1948 floats[16] = s4;floats[17] = t4;
1949 floats[18] = s3;floats[19] = t3;
1950 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1951 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1952 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1953 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1955 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1956 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1959 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1963 if(!r_draw2d.integer && !r_draw2d_force)
1965 DrawQ_ProcessDrawFlag(flags, hasalpha);
1967 // R_Mesh_ResetTextureState();
1968 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1970 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1971 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1974 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1978 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1979 if(!r_draw2d.integer && !r_draw2d_force)
1983 switch(vid.renderpath)
1985 case RENDERPATH_GL11:
1986 case RENDERPATH_GL13:
1987 case RENDERPATH_GL20:
1990 qglBegin(GL_LINE_LOOP);
1991 for (num = 0;num < mesh->num_vertices;num++)
1993 if (mesh->data_color4f)
1994 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]);
1995 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
2001 case RENDERPATH_D3D9:
2002 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2004 case RENDERPATH_D3D10:
2005 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2007 case RENDERPATH_D3D11:
2008 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2010 case RENDERPATH_SOFT:
2011 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2013 case RENDERPATH_GLES1:
2014 case RENDERPATH_GLES2:
2015 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2020 //[515]: this is old, delete
2021 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
2023 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
2024 if(!r_draw2d.integer && !r_draw2d_force)
2027 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
2029 switch(vid.renderpath)
2031 case RENDERPATH_GL11:
2032 case RENDERPATH_GL13:
2033 case RENDERPATH_GL20:
2037 //qglLineWidth(width);CHECKGLERROR
2039 GL_Color(r,g,b,alpha);
2042 qglVertex2f(x1, y1);
2043 qglVertex2f(x2, y2);
2048 case RENDERPATH_D3D9:
2049 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2051 case RENDERPATH_D3D10:
2052 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2054 case RENDERPATH_D3D11:
2055 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2057 case RENDERPATH_SOFT:
2058 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2060 case RENDERPATH_GLES1:
2061 case RENDERPATH_GLES2:
2062 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2067 void DrawQ_Lines (float width, int numlines, const float *vertex3f, const float *color4f, int flags)
2070 qboolean hasalpha = false;
2071 for (i = 0;i < numlines*2;i++)
2072 if (color4f[i*4+3] < 1.0f)
2075 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, hasalpha ? 0.5f : 1.0f);
2077 if(!r_draw2d.integer && !r_draw2d_force)
2080 switch(vid.renderpath)
2082 case RENDERPATH_GL11:
2083 case RENDERPATH_GL13:
2084 case RENDERPATH_GL20:
2087 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
2089 //qglLineWidth(width);CHECKGLERROR
2092 R_Mesh_PrepareVertices_Generic_Arrays(numlines*2, vertex3f, color4f, NULL);
2093 qglDrawArrays(GL_LINES, 0, numlines*2);
2096 case RENDERPATH_D3D9:
2097 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2099 case RENDERPATH_D3D10:
2100 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2102 case RENDERPATH_D3D11:
2103 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2105 case RENDERPATH_SOFT:
2106 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2108 case RENDERPATH_GLES1:
2109 case RENDERPATH_GLES2:
2110 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2115 void DrawQ_SetClipArea(float x, float y, float width, float height)
2120 // We have to convert the con coords into real coords
2121 // OGL uses top to bottom
2122 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
2123 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
2124 iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
2125 ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
2126 switch(vid.renderpath)
2128 case RENDERPATH_GL11:
2129 case RENDERPATH_GL13:
2130 case RENDERPATH_GL20:
2131 case RENDERPATH_GLES1:
2132 case RENDERPATH_GLES2:
2133 case RENDERPATH_SOFT:
2134 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
2136 case RENDERPATH_D3D9:
2137 GL_Scissor(ix, iy, iw, ih);
2139 case RENDERPATH_D3D10:
2140 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2142 case RENDERPATH_D3D11:
2143 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2147 GL_ScissorTest(true);
2150 void DrawQ_ResetClipArea(void)
2153 GL_ScissorTest(false);
2156 void DrawQ_Finish(void)
2158 r_refdef.draw2dstage = 0;
2161 void DrawQ_RecalcView(void)
2163 if(r_refdef.draw2dstage)
2164 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2167 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2168 void R_DrawGamma(void)
2171 switch(vid.renderpath)
2173 case RENDERPATH_GL20:
2174 case RENDERPATH_D3D9:
2175 case RENDERPATH_D3D10:
2176 case RENDERPATH_D3D11:
2177 case RENDERPATH_GLES2:
2178 if (vid_usinghwgamma || v_glslgamma.integer)
2181 case RENDERPATH_GL11:
2182 case RENDERPATH_GL13:
2183 if (vid_usinghwgamma)
2186 case RENDERPATH_GLES1:
2187 case RENDERPATH_SOFT:
2190 // all the blends ignore depth
2191 // R_Mesh_ResetTextureState();
2192 R_SetupShader_Generic_NoTexture(true, true);
2194 GL_DepthRange(0, 1);
2195 GL_PolygonOffset(0, 0);
2196 GL_DepthTest(false);
2198 // interpretation of brightness and contrast:
2199 // color range := brightness .. (brightness + contrast)
2200 // i.e. "c *= contrast; c += brightness"
2201 // plausible values for brightness thus range from -contrast to 1
2203 // apply pre-brightness (subtractive brightness, for where contrast was >= 1)
2204 if (vid.support.ext_blend_subtract)
2206 if (v_color_enable.integer)
2208 c[0] = -v_color_black_r.value / v_color_white_r.value;
2209 c[1] = -v_color_black_g.value / v_color_white_g.value;
2210 c[2] = -v_color_black_b.value / v_color_white_b.value;
2213 c[0] = c[1] = c[2] = -v_brightness.value / v_contrast.value;
2214 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2216 // need SUBTRACTIVE blending to do this!
2217 GL_BlendEquationSubtract(true);
2218 GL_BlendFunc(GL_ONE, GL_ONE);
2219 GL_Color(c[0], c[1], c[2], 1);
2220 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2221 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2222 GL_BlendEquationSubtract(false);
2227 if (v_color_enable.integer)
2229 c[0] = v_color_white_r.value;
2230 c[1] = v_color_white_g.value;
2231 c[2] = v_color_white_b.value;
2234 c[0] = c[1] = c[2] = v_contrast.value;
2235 if (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2237 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2238 while (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2241 cc[0] = bound(0, c[0] - 1, 1);
2242 cc[1] = bound(0, c[1] - 1, 1);
2243 cc[2] = bound(0, c[2] - 1, 1);
2244 GL_Color(cc[0], cc[1], cc[2], 1);
2245 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2246 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2252 if (c[0] <= 0.997f || c[1] <= 0.997f || c[2] <= 0.997f)
2254 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
2255 GL_Color(c[0], c[1], c[2], 1);
2256 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2257 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2260 // apply post-brightness (additive brightness, for where contrast was <= 1)
2261 if (v_color_enable.integer)
2263 c[0] = v_color_black_r.value;
2264 c[1] = v_color_black_g.value;
2265 c[2] = v_color_black_b.value;
2268 c[0] = c[1] = c[2] = v_brightness.value;
2269 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2271 GL_BlendFunc(GL_ONE, GL_ONE);
2272 GL_Color(c[0], c[1], c[2], 1);
2273 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2274 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);