]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - gl_draw.c
overhauling a lot of functions to allow fbo rendering
[xonotic/darkplaces.git] / gl_draw.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
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.
8
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.
12
13 See the GNU General Public License for more details.
14
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.
18
19 */
20
21 #include "quakedef.h"
22 #include "image.h"
23 #include "wad.h"
24
25 #include "cl_video.h"
26 #include "cl_dyntexture.h"
27
28 #include "ft2.h"
29 #include "ft2_fontdefs.h"
30
31 dp_fonts_t dp_fonts;
32 static mempool_t *fonts_mempool = NULL;
33
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)"};
37
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" */};
45
46 extern cvar_t v_glslgamma;
47
48 //=============================================================================
49 /* Support Routines */
50
51 #define FONT_FILESIZE 13468
52 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
53 static cachepic_t cachepics[MAX_CACHED_PICS];
54 static int numcachepics;
55
56 rtexturepool_t *drawtexturepool;
57
58 static const unsigned char concharimage[FONT_FILESIZE] =
59 {
60 #include "lhfont.h"
61 };
62
63 static rtexture_t *draw_generateconchars(void)
64 {
65         int i;
66         unsigned char *data;
67         double random;
68         rtexture_t *tex;
69
70         data = LoadTGA_BGRA (concharimage, FONT_FILESIZE, NULL);
71 // Gold numbers
72         for (i = 0;i < 8192;i++)
73         {
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);
79         }
80 // White chars
81         for (i = 8192;i < 32768;i++)
82         {
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);
88         }
89 // Gold numbers
90         for (i = 32768;i < 40960;i++)
91         {
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);
97         }
98 // Red chars
99         for (i = 40960;i < 65536;i++)
100         {
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);
106         }
107
108 #if 0
109         Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, data);
110 #endif
111
112         tex = R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, data, TEXTYPE_BGRA, TEXF_ALPHA, -1, NULL);
113         Mem_Free(data);
114         return tex;
115 }
116
117 static rtexture_t *draw_generateditherpattern(void)
118 {
119         int x, y;
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);
125 }
126
127 typedef struct embeddedpic_s
128 {
129         const char *name;
130         int width;
131         int height;
132         const char *pixels;
133 }
134 embeddedpic_t;
135
136 static const embeddedpic_t embeddedpics[] =
137 {
138         {
139         "gfx/prydoncursor001", 16, 16,
140         "477777774......."
141         "77.....6........"
142         "7.....6........."
143         "7....6.........."
144         "7.....6........."
145         "7..6...6........"
146         "7.6.6...6......."
147         "76...6...6......"
148         "4.....6.6......."
149         ".......6........"
150         "................"
151         "................"
152         "................"
153         "................"
154         "................"
155         "................"
156         },
157         {
158         "ui/mousepointer", 16, 16,
159         "477777774......."
160         "77.....6........"
161         "7.....6........."
162         "7....6.........."
163         "7.....6........."
164         "7..6...6........"
165         "7.6.6...6......."
166         "76...6...6......"
167         "4.....6.6......."
168         ".......6........"
169         "................"
170         "................"
171         "................"
172         "................"
173         "................"
174         "................"
175         },
176         {
177         "gfx/crosshair1", 16, 16,
178         "................"
179         "................"
180         "................"
181         "...33......33..."
182         "...355....553..."
183         "....577..775...."
184         ".....77..77....."
185         "................"
186         "................"
187         ".....77..77....."
188         "....577..775...."
189         "...355....553..."
190         "...33......33..."
191         "................"
192         "................"
193         "................"
194         },
195         {
196         "gfx/crosshair2", 16, 16,
197         "................"
198         "................"
199         "................"
200         "...3........3..."
201         "....5......5...."
202         ".....7....7....."
203         "......7..7......"
204         "................"
205         "................"
206         "......7..7......"
207         ".....7....7....."
208         "....5......5...."
209         "...3........3..."
210         "................"
211         "................"
212         "................"
213         },
214         {
215         "gfx/crosshair3", 16, 16,
216         "................"
217         ".......77......."
218         ".......77......."
219         "................"
220         "................"
221         ".......44......."
222         ".......44......."
223         ".77..44..44..77."
224         ".77..44..44..77."
225         ".......44......."
226         ".......44......."
227         "................"
228         "................"
229         ".......77......."
230         ".......77......."
231         "................"
232         },
233         {
234         "gfx/crosshair4", 16, 16,
235         "................"
236         "................"
237         "................"
238         "................"
239         "................"
240         "................"
241         "................"
242         "................"
243         "........7777777."
244         "........752....."
245         "........72......"
246         "........7......."
247         "........7......."
248         "........7......."
249         "........7......."
250         "................"
251         },
252         {
253         "gfx/crosshair5", 8, 8,
254         "........"
255         "........"
256         "....7..."
257         "........"
258         "..7.7.7."
259         "........"
260         "....7..."
261         "........"
262         },
263         {
264         "gfx/crosshair6", 2, 2,
265         "77"
266         "77"
267         },
268         {
269         "gfx/crosshair7", 16, 16,
270         "................"
271         ".3............3."
272         "..5...2332...5.."
273         "...7.3....3.7..."
274         "....7......7...."
275         "...3.7....7.3..."
276         "..2...7..7...2.."
277         "..3..........3.."
278         "..3..........3.."
279         "..2...7..7...2.."
280         "...3.7....7.3..."
281         "....7......7...."
282         "...7.3....3.7..."
283         "..5...2332...5.."
284         ".3............3."
285         "................"
286         },
287         {NULL, 0, 0, NULL}
288 };
289
290 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
291 {
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();
300         if (!quiet)
301                 Con_DPrintf("Draw_CachePic: failed to load %s\n", name);
302         return r_texture_notexture;
303 }
304
305 int draw_frame = 1;
306
307 /*
308 ================
309 Draw_CachePic
310 ================
311 */
312 // FIXME: move this to client somehow
313 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
314 {
315         int crc, hashkey;
316         unsigned char *pixels = NULL;
317         cachepic_t *pic;
318         fs_offset_t lmpsize;
319         unsigned char *lmpdata;
320         char lmpname[MAX_QPATH];
321         int texflags;
322         int j;
323         qboolean ddshasalpha;
324         float ddsavgcolor[4];
325         qboolean loaded = false;
326
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;
332
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)
337         {
338                 if (!strcmp (path, pic->name))
339                 {
340                         // if it was created (or replaced) by Draw_NewPic, just return it
341                         if(pic->flags & CACHEPICFLAG_NEWPIC)
342                                 return pic;
343                         if (!((pic->texflags ^ texflags) & ~(TEXF_COMPRESS))) // ignore TEXF_COMPRESS when comparing, because fallback pics remove the flag
344                         {
345                                 if(!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
346                                 {
347                                         if(pic->tex)
348                                                 pic->autoload = false; // persist it
349                                         else
350                                                 goto reload; // load it below, and then persist
351                                 }
352                                 return pic;
353                         }
354                 }
355         }
356
357         if (numcachepics == MAX_CACHED_PICS)
358         {
359                 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
360                 // FIXME: support NULL in callers?
361                 return cachepics; // return the first one
362         }
363         pic = cachepics + (numcachepics++);
364         memset(pic, 0, sizeof(*pic));
365         strlcpy (pic->name, path, sizeof(pic->name));
366         // link into list
367         pic->chain = cachepichash[hashkey];
368         cachepichash[hashkey] = pic;
369
370 reload:
371         // TODO why does this crash?
372         if(pic->allow_free_tex && pic->tex)
373                 R_PurgeTexture(pic->tex);
374
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
379         if( pic->tex ) {
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)
384                 return pic;
385         }
386
387         pic->allow_free_tex = true;
388
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;
393
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)))
396         {
397                 // note this loads even if autoload is true, otherwise we can't get the width/height
398                 loaded = true;
399                 pic->hasalpha = ddshasalpha;
400                 pic->width = R_TextureWidth(pic->tex);
401                 pic->height = R_TextureHeight(pic->tex);
402         }
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)))))
404         {
405                 loaded = true;
406                 pic->hasalpha = false;
407                 if (pic->texflags & TEXF_ALPHA)
408                 {
409                         for (j = 3;j < image_width * image_height * 4;j += 4)
410                         {
411                                 if (pixels[j] < 255)
412                                 {
413                                         pic->hasalpha = true;
414                                         break;
415                                 }
416                         }
417                 }
418
419                 pic->width = image_width;
420                 pic->height = image_height;
421                 if (!pic->autoload)
422                 {
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);
424 #ifndef USE_GLES2
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);
427 #endif
428                 }
429         }
430         if (!loaded)
431         {
432                 pic->autoload = false;
433                 // never compress the fallback images
434                 pic->texflags &= ~TEXF_COMPRESS;
435         }
436
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)))
443         {
444                 if (developer_loading.integer)
445                         Con_Printf("loading lump \"%s\"\n", pic->name);
446
447                 if (lmpsize >= 9)
448                 {
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
452                         if (!loaded)
453                         {
454                                 loaded = true;
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);
456                         }
457                 }
458                 Mem_Free(lmpdata);
459         }
460         else if ((lmpdata = W_GetLumpName (pic->name + 4)))
461         {
462                 if (developer_loading.integer)
463                         Con_Printf("loading gfx.wad lump \"%s\"\n", pic->name + 4);
464
465                 if (!strcmp(pic->name, "gfx/conchars"))
466                 {
467                         // conchars is a raw image and with color 0 as transparent instead of 255
468                         pic->width = 128;
469                         pic->height = 128;
470                         // if no high quality replacement image was found, upload the original low quality texture
471                         if (!loaded)
472                         {
473                                 loaded = true;
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);
475                         }
476                 }
477                 else
478                 {
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
482                         if (!loaded)
483                         {
484                                 loaded = true;
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);
486                         }
487                 }
488         }
489
490         if (pixels)
491         {
492                 Mem_Free(pixels);
493                 pixels = NULL;
494         }
495         if (!loaded)
496         {
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);
502         }
503
504         return pic;
505 }
506
507 cachepic_t *Draw_CachePic (const char *path)
508 {
509         return Draw_CachePic_Flags (path, 0); // default to persistent!
510 }
511
512 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
513 {
514         if (pic->autoload && !pic->tex)
515         {
516                 if (pic->tex == NULL && r_texture_dds_load.integer != 0)
517                 {
518                         qboolean ddshasalpha;
519                         float ddsavgcolor[4];
520                         pic->tex = R_LoadTextureDDSFile(drawtexturepool, va("dds/%s.dds", pic->name), pic->texflags, &ddshasalpha, ddsavgcolor, 0);
521                 }
522                 if (pic->tex == NULL)
523                 {
524                         pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, vid.sRGB2D);
525 #ifndef USE_GLES2
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);
528 #endif
529                 }
530                 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
531                 {
532                         pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, vid.sRGB2D);
533 #ifndef USE_GLES2
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);
536 #endif
537                 }
538                 if (pic->tex == NULL)
539                         pic->tex = draw_generatepic(pic->name, true);
540         }
541         pic->lastusedframe = draw_frame;
542         return pic->tex;
543 }
544
545 void Draw_Frame(void)
546 {
547         int i;
548         cachepic_t *pic;
549         static double nextpurgetime;
550         if (nextpurgetime > realtime)
551                 return;
552         nextpurgetime = realtime + 0.05;
553         for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
554         {
555                 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
556                 {
557                         R_FreeTexture(pic->tex);
558                         pic->tex = NULL;
559                 }
560         }
561         draw_frame++;
562 }
563
564 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
565 {
566         int crc, hashkey;
567         cachepic_t *pic;
568
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))
573                         break;
574
575         if (pic)
576         {
577                 if (pic->flags == CACHEPICFLAG_NEWPIC && pic->tex && pic->width == width && pic->height == height)
578                 {
579                         R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, 0, width, height, 1);
580                         return pic;
581                 }
582         }
583         else
584         {
585                 if (numcachepics == MAX_CACHED_PICS)
586                 {
587                         Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
588                         // FIXME: support NULL in callers?
589                         return cachepics; // return the first one
590                 }
591                 pic = cachepics + (numcachepics++);
592                 memset(pic, 0, sizeof(*pic));
593                 strlcpy (pic->name, picname, sizeof(pic->name));
594                 // link into list
595                 pic->chain = cachepichash[hashkey];
596                 cachepichash[hashkey] = pic;
597         }
598
599         pic->flags = CACHEPICFLAG_NEWPIC; // disable texflags checks in Draw_CachePic
600         pic->width = width;
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);
605         return pic;
606 }
607
608 void Draw_FreePic(const char *picname)
609 {
610         int crc;
611         int hashkey;
612         cachepic_t *pic;
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)
617         {
618                 if (!strcmp (picname, pic->name) && pic->tex)
619                 {
620                         R_FreeTexture(pic->tex);
621                         pic->tex = NULL;
622                         pic->width = 0;
623                         pic->height = 0;
624                         return;
625                 }
626         }
627 }
628
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)
632 {
633         int i, ch;
634         float maxwidth;
635         char widthfile[MAX_QPATH];
636         char *widthbuf;
637         fs_offset_t widthbufsize;
638
639         if(override || !fnt->texpath[0])
640         {
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;
652         }
653         // fix bad scale
654         if (fnt->settings.scale <= 0)
655                 fnt->settings.scale = 1;
656
657         if(drawtexturepool == NULL)
658                 return; // before gl_draw_start, so will be loaded later
659
660         if(fnt->ft2)
661         {
662                 // clear freetype font
663                 Font_UnloadFont(fnt->ft2);
664                 Mem_Free(fnt->ft2);
665                 fnt->ft2 = NULL;
666         }
667
668         if(fnt->req_face != -1)
669         {
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);
672         }
673
674         fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
675         if(fnt->tex == r_texture_notexture)
676         {
677                 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
678                 {
679                         if (!fnt->fallbacks[i][0])
680                                 break;
681                         fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
682                         if(fnt->tex != r_texture_notexture)
683                                 break;
684                 }
685                 if(fnt->tex == r_texture_notexture)
686                 {
687                         fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
688                         strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
689                 }
690                 else
691                         dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
692         }
693         else
694                 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
695
696         // unspecified width == 1 (base width)
697         for(ch = 0; ch < 256; ++ch)
698                 fnt->width_of[ch] = 1;
699
700         // FIXME load "name.width", if it fails, fill all with 1
701         if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
702         {
703                 float extraspacing = 0;
704                 const char *p = widthbuf;
705
706                 ch = 0;
707                 while(ch < 256)
708                 {
709                         if(!COM_ParseToken_Simple(&p, false, false))
710                                 return;
711
712                         switch(*com_token)
713                         {
714                                 case '0':
715                                 case '1':
716                                 case '2':
717                                 case '3':
718                                 case '4':
719                                 case '5':
720                                 case '6':
721                                 case '7':
722                                 case '8':
723                                 case '9':
724                                 case '+':
725                                 case '-':
726                                 case '.':
727                                         fnt->width_of[ch] = atof(com_token) + extraspacing;
728                                         ch++;
729                                         break;
730                                 default:
731                                         if(!strcmp(com_token, "extraspacing"))
732                                         {
733                                                 if(!COM_ParseToken_Simple(&p, false, false))
734                                                         return;
735                                                 extraspacing = atof(com_token);
736                                         }
737                                         else if(!strcmp(com_token, "scale"))
738                                         {
739                                                 if(!COM_ParseToken_Simple(&p, false, false))
740                                                         return;
741                                                 fnt->settings.scale = atof(com_token);
742                                         }
743                                         else
744                                         {
745                                                 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
746                                                 if(!COM_ParseToken_Simple(&p, false, false))
747                                                         return;
748                                         }
749                                         break;
750                         }
751                 }
752
753                 Mem_Free(widthbuf);
754         }
755
756         if(fnt->ft2)
757         {
758                 for (i = 0; i < MAX_FONT_SIZES; ++i)
759                 {
760                         ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
761                         if (!map)
762                                 break;
763                         for(ch = 0; ch < 256; ++ch)
764                                 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
765                 }
766         }
767
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;
772
773         // fix up maxwidth for overlap
774         fnt->maxwidth *= fnt->settings.scale;
775
776         if(fnt == FONT_CONSOLE)
777                 con_linewidth = -1; // rewrap console in next frame
778 }
779
780 extern cvar_t developer_font;
781 dp_font_t *FindFont(const char *title, qboolean allocate_new)
782 {
783         int i, oldsize;
784
785         // find font
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
790         if (allocate_new)
791         {
792                 // find any font with empty title
793                 for(i = 0; i < dp_fonts.maxsize; ++i)
794                 {
795                         if(!strcmp(dp_fonts.f[i].title, ""))
796                         {
797                                 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
798                                 return &dp_fonts.f[i];
799                         }
800                 }
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];
814         }
815         return NULL;
816 }
817
818 static float snap_to_pixel_x(float x, float roundUpAt)
819 {
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);
824         /*
825         x = (int)(x * vid.width / vid_conwidth.value);
826         x = (x * vid_conwidth.value / vid.width);
827         return x;
828         */
829 }
830
831 static float snap_to_pixel_y(float y, float roundUpAt)
832 {
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);
837         /*
838         y = (int)(y * vid.height / vid_conheight.value);
839         y = (y * vid_conheight.value / vid.height);
840         return y;
841         */
842 }
843
844 static void LoadFont_f(void)
845 {
846         dp_font_t *f;
847         int i, sizes;
848         const char *filelist, *c, *cm;
849         float sz, scale, voffset;
850         char mainfont[MAX_QPATH];
851
852         if(Cmd_Argc() < 2)
853         {
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"
866                            "custom switches:\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"
869                         );
870                 return;
871         }
872         f = FindFont(Cmd_Argv(1), true);
873         if(f == NULL)
874         {
875                 Con_Printf("font function not found\n");
876                 return;
877         }
878
879         if(Cmd_Argc() < 3)
880                 filelist = "gfx/conchars";
881         else
882                 filelist = Cmd_Argv(2);
883
884         memset(f->fallbacks, 0, sizeof(f->fallbacks));
885         memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
886
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);
892         else
893         {
894                 f->req_face = 0;
895                 c = cm;
896         }
897
898         if(!c || (c - filelist) > MAX_QPATH)
899                 strlcpy(mainfont, filelist, sizeof(mainfont));
900         else
901         {
902                 memcpy(mainfont, filelist, c - filelist);
903                 mainfont[c - filelist] = 0;
904         }
905
906         for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
907         {
908                 c = strchr(filelist, ',');
909                 if(!c)
910                         break;
911                 filelist = c + 1;
912                 if(!*filelist)
913                         break;
914                 c = strchr(filelist, ':');
915                 cm = strchr(filelist, ',');
916                 if(c && (!cm || c < cm))
917                         f->fallback_faces[i] = atoi(c+1);
918                 else
919                 {
920                         f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
921                         c = cm;
922                 }
923                 if(!c || (c-filelist) > MAX_QPATH)
924                 {
925                         strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
926                 }
927                 else
928                 {
929                         memcpy(f->fallbacks[i], filelist, c - filelist);
930                         f->fallbacks[i][c - filelist] = 0;
931                 }
932         }
933
934         // for now: by default load only one size: the default size
935         f->req_sizes[0] = 0;
936         for(i = 1; i < MAX_FONT_SIZES; ++i)
937                 f->req_sizes[i] = -1;
938
939         scale = 1;
940         voffset = 0;
941         if(Cmd_Argc() >= 4)
942         {
943                 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
944                 {
945                         // special switches
946                         if (!strcmp(Cmd_Argv(i), "scale"))
947                         {
948                                 i++;
949                                 if (i < Cmd_Argc())
950                                         scale = atof(Cmd_Argv(i));
951                                 continue;
952                         }
953                         if (!strcmp(Cmd_Argv(i), "voffset"))
954                         {
955                                 i++;
956                                 if (i < Cmd_Argc())
957                                         voffset = atof(Cmd_Argv(i));
958                                 continue;
959                         }
960
961                         if (sizes == -1)
962                                 continue; // no slot for other sizes
963
964                         // parse one of sizes
965                         sz = atof(Cmd_Argv(i));
966                         if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
967                         {
968                                 // search for duplicated sizes
969                                 int j;
970                                 for (j=0; j<sizes; j++)
971                                         if (f->req_sizes[j] == sz)
972                                                 break;
973                                 if (j != sizes)
974                                         continue; // sz already in req_sizes, don't add it again
975
976                                 if (sizes == MAX_FONT_SIZES)
977                                 {
978                                         Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
979                                         sizes = -1;
980                                         continue;
981                                 }
982                                 f->req_sizes[sizes] = sz;
983                                 sizes++;
984                         }
985                 }
986         }
987
988         LoadFont(true, mainfont, f, scale, voffset);
989 }
990
991 /*
992 ===============
993 Draw_Init
994 ===============
995 */
996 static void gl_draw_start(void)
997 {
998         int i;
999         drawtexturepool = R_AllocTexturePool();
1000
1001         numcachepics = 0;
1002         memset(cachepichash, 0, sizeof(cachepichash));
1003
1004         font_start();
1005
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);
1010
1011         // draw the loading screen so people have something to see in the newly opened window
1012         SCR_UpdateLoadingScreen(true);
1013 }
1014
1015 static void gl_draw_shutdown(void)
1016 {
1017         font_shutdown();
1018
1019         R_FreeTexturePool(&drawtexturepool);
1020
1021         numcachepics = 0;
1022         memset(cachepichash, 0, sizeof(cachepichash));
1023 }
1024
1025 static void gl_draw_newmap(void)
1026 {
1027         font_newmap();
1028 }
1029
1030 void GL_Draw_Init (void)
1031 {
1032         int i, j;
1033
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);
1044
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);
1050
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++);
1064
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);
1067 }
1068
1069 static void _DrawQ_Setup(void)
1070 {
1071         r_viewport_t viewport;
1072         if (r_refdef.draw2dstage == 1)
1073                 return;
1074         r_refdef.draw2dstage = 1;
1075         CHECKGLERROR
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);
1084
1085         GL_DepthRange(0, 1);
1086         GL_PolygonOffset(0, 0);
1087         GL_DepthTest(false);
1088         GL_Color(1,1,1,1);
1089 }
1090
1091 qboolean r_draw2d_force = false;
1092 void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1093 {
1094         _DrawQ_Setup();
1095         CHECKGLERROR
1096         if(!r_draw2d.integer && !r_draw2d_force)
1097                 return;
1098         DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1099 }
1100 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1101 {
1102         if(flags == DRAWFLAG_ADDITIVE)
1103         {
1104                 GL_DepthMask(false);
1105                 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1106         }
1107         else if(flags == DRAWFLAG_MODULATE)
1108         {
1109                 GL_DepthMask(false);
1110                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1111         }
1112         else if(flags == DRAWFLAG_2XMODULATE)
1113         {
1114                 GL_DepthMask(false);
1115                 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1116         }
1117         else if(flags == DRAWFLAG_SCREEN)
1118         {
1119                 GL_DepthMask(false);
1120                 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1121         }
1122         else if(alpha)
1123         {
1124                 GL_DepthMask(false);
1125                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1126         }
1127         else
1128         {
1129                 GL_DepthMask(true);
1130                 GL_BlendFunc(GL_ONE, GL_ZERO);
1131         }
1132 }
1133
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)
1135 {
1136         float floats[36];
1137
1138         _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1139         if(!r_draw2d.integer && !r_draw2d_force)
1140                 return;
1141
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;
1151         if (pic)
1152         {
1153                 if (width == 0)
1154                         width = pic->width;
1155                 if (height == 0)
1156                         height = pic->height;
1157                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1158
1159 #if 0
1160       // AK07: lets be texel correct on the corners
1161       {
1162          float horz_offset = 0.5f / pic->width;
1163          float vert_offset = 0.5f / pic->height;
1164
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;
1169       }
1170 #endif
1171         }
1172         else
1173                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1174
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;
1180
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);
1183 }
1184
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)
1186 {
1187         float floats[36];
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);
1194
1195         _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1196         if(!r_draw2d.integer && !r_draw2d_force)
1197                 return;
1198
1199 //      R_Mesh_ResetTextureState();
1200         if (pic)
1201         {
1202                 if (width == 0)
1203                         width = pic->width;
1204                 if (height == 0)
1205                         height = pic->height;
1206                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1207         }
1208         else
1209                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1210
1211         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1212
1213 // top left
1214         floats[0] = x - cosaf*org_x - cosar*org_y;
1215         floats[1] = y - sinaf*org_x - sinar*org_y;
1216
1217 // top right
1218         floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1219         floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1220
1221 // bottom right
1222         floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1223         floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1224
1225 // bottom left
1226         floats[9]  = x - cosaf*org_x + cosar*(height-org_y);
1227         floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1228
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;
1237
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);
1240 }
1241
1242 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1243 {
1244         float floats[36];
1245
1246         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1247         if(!r_draw2d.integer && !r_draw2d_force)
1248                 return;
1249
1250 //      R_Mesh_ResetTextureState();
1251         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1252
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;
1266
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);
1269 }
1270
1271 /// color tag printing
1272 static const vec4_t string_colors[] =
1273 {
1274         // Quake3 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}
1297 };
1298
1299 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
1300
1301 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1302 {
1303         float C = r_textcontrast.value;
1304         float B = r_textbrightness.value;
1305         if (colorindex & 0x10000) // that bit means RGB color
1306         {
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;
1311         }
1312         else
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);
1315         if (shadow)
1316         {
1317                 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1318                 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1319         }
1320 }
1321
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)
1324 {
1325         const char *text_start = text;
1326         int colorindex = STRING_COLOR_DEFAULT;
1327         size_t i;
1328         float x = 0;
1329         Uchar ch, mapch, nextch;
1330         Uchar prevch = 0; // used for kerning
1331         int tempcolorindex;
1332         float kx;
1333         int map_index = 0;
1334         size_t bytes_left;
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;
1339         // float ftbase_x;
1340         qboolean snap = true;
1341         qboolean least_one = false;
1342         float dw; // display w
1343         //float dh; // display h
1344         const float *width_of;
1345
1346         if (!h) h = w;
1347         if (!h) {
1348                 w = h = 1;
1349                 snap = false;
1350         }
1351         // do this in the end
1352         w *= fnt->settings.scale;
1353         h *= fnt->settings.scale;
1354
1355         // find the most fitting size:
1356         if (ft2 != NULL)
1357         {
1358                 if (snap)
1359                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1360                 else
1361                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1362                 fontmap = Font_MapForIndex(ft2, map_index);
1363         }
1364
1365         dw = w * sw;
1366         //dh = h * sh;
1367
1368         if (*maxlen < 1)
1369                 *maxlen = 1<<30;
1370
1371         if (!outcolor || *outcolor == -1)
1372                 colorindex = STRING_COLOR_DEFAULT;
1373         else
1374                 colorindex = *outcolor;
1375
1376         // maxwidth /= fnt->scale; // w and h are multiplied by it already
1377         // ftbase_x = snap_to_pixel_x(0);
1378         
1379         if(maxwidth <= 0)
1380         {
1381                 least_one = true;
1382                 maxwidth = -maxwidth;
1383         }
1384
1385         //if (snap)
1386         //      x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1387
1388         if (fontmap)
1389                 width_of = fontmap->width_of;
1390         else
1391                 width_of = fnt->width_of;
1392
1393         for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1394         {
1395                 size_t i0 = i;
1396                 nextch = ch = u8_getnchar(text, &text, bytes_left);
1397                 i = text - text_start;
1398                 if (!ch)
1399                         break;
1400                 if (ch == ' ' && !fontmap)
1401                 {
1402                         if(!least_one || i0) // never skip the first character
1403                         if(x + width_of[(int) ' '] * dw > maxwidth)
1404                         {
1405                                 i = i0;
1406                                 break; // oops, can't draw this
1407                         }
1408                         x += width_of[(int) ' '] * dw;
1409                         continue;
1410                 }
1411                 // i points to the char after ^
1412                 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1413                 {
1414                         ch = *text; // colors are ascii, so no u8_ needed
1415                         if (ch <= '9' && ch >= '0') // ^[0-9] found
1416                         {
1417                                 colorindex = ch - '0';
1418                                 ++text;
1419                                 ++i;
1420                                 continue;
1421                         }
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
1426                         {
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;
1433                                 if (tempcolorindex)
1434                                 {
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;
1439                                         if (tempcolorindex)
1440                                         {
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;
1445                                                 if (tempcolorindex)
1446                                                 {
1447                                                         colorindex = tempcolorindex | 0xf;
1448                                                         // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1449                                                         i+=4;
1450                                                         text += 4;
1451                                                         continue;
1452                                                 }
1453                                         }
1454                                 }
1455                         }
1456                         else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1457                         {
1458                                 i++;
1459                                 text++;
1460                         }
1461                         i--;
1462                 }
1463                 ch = nextch;
1464
1465                 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1466                 {
1467                         if (ch > 0xE000)
1468                                 ch -= 0xE000;
1469                         if (ch > 0xFF)
1470                                 continue;
1471                         if (fontmap)
1472                                 map = ft2_oldstyle_map;
1473                         prevch = 0;
1474                         if(!least_one || i0) // never skip the first character
1475                         if(x + width_of[ch] * dw > maxwidth)
1476                         {
1477                                 i = i0;
1478                                 break; // oops, can't draw this
1479                         }
1480                         x += width_of[ch] * dw;
1481                 } else {
1482                         if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1483                         {
1484                                 map = FontMap_FindForChar(fontmap, ch);
1485                                 if (!map)
1486                                 {
1487                                         if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1488                                                 break;
1489                                         if (!map)
1490                                                 break;
1491                                 }
1492                         }
1493                         mapch = ch - map->start;
1494                         if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1495                                 x += kx * dw;
1496                         x += map->glyphs[mapch].advance_x * dw;
1497                         //prevmap = map;
1498                         prevch = ch;
1499                 }
1500         }
1501
1502         *maxlen = i;
1503
1504         if (outcolor)
1505                 *outcolor = colorindex;
1506
1507         return x;
1508 }
1509
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)
1512 {
1513         int shadow, colorindex = STRING_COLOR_DEFAULT;
1514         size_t i;
1515         float x = startx, y, s, t, u, v, thisw;
1516         float *av, *at, *ac;
1517         int batchcount;
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
1523         int tempcolorindex;
1524         int map_index = 0;
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
1528         float ftbase_y;
1529         const char *text_start = text;
1530         float kx, ky;
1531         ft2_font_t *ft2 = fnt->ft2;
1532         qboolean snap = true;
1533         float pix_x, pix_y;
1534         size_t bytes_left;
1535         float dw, dh;
1536         const float *width_of;
1537
1538         int tw, th;
1539         tw = R_TextureWidth(fnt->tex);
1540         th = R_TextureHeight(fnt->tex);
1541
1542         if (!h) h = w;
1543         if (!h) {
1544                 h = w = 1;
1545                 snap = false;
1546         }
1547
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;
1551
1552         if (ft2 != NULL)
1553         {
1554                 if (snap)
1555                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1556                 else
1557                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1558                 fontmap = Font_MapForIndex(ft2, map_index);
1559         }
1560
1561         dw = w * sw;
1562         dh = h * sh;
1563
1564         // draw the font at its baseline when using freetype
1565         //ftbase_x = 0;
1566         ftbase_y = dh * (4.5/6.0);
1567
1568         if (maxlen < 1)
1569                 maxlen = 1<<30;
1570
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);
1574
1575 //      R_Mesh_ResetTextureState();
1576         if (!fontmap)
1577                 R_Mesh_TexBind(0, fnt->tex);
1578         R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1579
1580         ac = color4f;
1581         at = texcoord2f;
1582         av = vertex3f;
1583         batchcount = 0;
1584
1585         //ftbase_x = snap_to_pixel_x(ftbase_x);
1586         if(snap)
1587         {
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);
1591         }
1592
1593         pix_x = vid.width / vid_conwidth.value;
1594         pix_y = vid.height / vid_conheight.value;
1595
1596         if (fontmap)
1597                 width_of = fontmap->width_of;
1598         else
1599                 width_of = fnt->width_of;
1600
1601         for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1602         {
1603                 prevch = 0;
1604                 text = text_start;
1605
1606                 if (!outcolor || *outcolor == -1)
1607                         colorindex = STRING_COLOR_DEFAULT;
1608                 else
1609                         colorindex = *outcolor;
1610
1611                 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1612
1613                 x = startx;
1614                 y = starty;
1615                 /*
1616                 if (shadow)
1617                 {
1618                         x += r_textshadow.value * vid.width / vid_conwidth.value;
1619                         y += r_textshadow.value * vid.height / vid_conheight.value;
1620                 }
1621                 */
1622                 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1623                 {
1624                         nextch = ch = u8_getnchar(text, &text, bytes_left);
1625                         i = text - text_start;
1626                         if (!ch)
1627                                 break;
1628                         if (ch == ' ' && !fontmap)
1629                         {
1630                                 x += width_of[(int) ' '] * dw;
1631                                 continue;
1632                         }
1633                         if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1634                         {
1635                                 ch = *text; // colors are ascii, so no u8_ needed
1636                                 if (ch <= '9' && ch >= '0') // ^[0-9] found
1637                                 {
1638                                         colorindex = ch - '0';
1639                                         DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1640                                         ++text;
1641                                         ++i;
1642                                         continue;
1643                                 }
1644                                 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1645                                 {
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;
1652                                         if (tempcolorindex)
1653                                         {
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;
1658                                                 if (tempcolorindex)
1659                                                 {
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;
1664                                                         if (tempcolorindex)
1665                                                         {
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);
1670                                                                 i+=4;
1671                                                                 text+=4;
1672                                                                 continue;
1673                                                         }
1674                                                 }
1675                                         }
1676                                 }
1677                                 else if (ch == STRING_COLOR_TAG)
1678                                 {
1679                                         i++;
1680                                         text++;
1681                                 }
1682                                 i--;
1683                         }
1684                         // get the backup
1685                         ch = nextch;
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)
1689                         if (shadow)
1690                         {
1691                                 x += 1.0/pix_x * r_textshadow.value;
1692                                 y += 1.0/pix_y * r_textshadow.value;
1693                         }
1694                         if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1695                         {
1696                                 if (ch >= 0xE000)
1697                                         ch -= 0xE000;
1698                                 if (ch > 0xFF)
1699                                         goto out;
1700                                 if (fontmap)
1701                                 {
1702                                         if (map != ft2_oldstyle_map)
1703                                         {
1704                                                 if (batchcount)
1705                                                 {
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);
1709                                                         batchcount = 0;
1710                                                         ac = color4f;
1711                                                         at = texcoord2f;
1712                                                         av = vertex3f;
1713                                                 }
1714                                                 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1715                                                 map = ft2_oldstyle_map;
1716                                         }
1717                                 }
1718                                 prevch = 0;
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;
1739                                 ac += 16;
1740                                 at += 8;
1741                                 av += 12;
1742                                 batchcount++;
1743                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1744                                 {
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);
1747                                         batchcount = 0;
1748                                         ac = color4f;
1749                                         at = texcoord2f;
1750                                         av = vertex3f;
1751                                 }
1752                                 x += width_of[ch] * dw;
1753                         } else {
1754                                 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1755                                 {
1756                                         // new charmap - need to render
1757                                         if (batchcount)
1758                                         {
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);
1762                                                 batchcount = 0;
1763                                                 ac = color4f;
1764                                                 at = texcoord2f;
1765                                                 av = vertex3f;
1766                                         }
1767                                         // find the new map
1768                                         map = FontMap_FindForChar(fontmap, ch);
1769                                         if (!map)
1770                                         {
1771                                                 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1772                                                 {
1773                                                         shadow = -1;
1774                                                         break;
1775                                                 }
1776                                                 if (!map)
1777                                                 {
1778                                                         // this shouldn't happen
1779                                                         shadow = -1;
1780                                                         break;
1781                                                 }
1782                                         }
1783                                         R_SetupShader_Generic(map->pic->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1784                                 }
1785
1786                                 mapch = ch - map->start;
1787                                 thisw = map->glyphs[mapch].advance_x;
1788
1789                                 //x += ftbase_x;
1790                                 y += ftbase_y;
1791                                 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1792                                 {
1793                                         x += kx * dw;
1794                                         y += ky * dh;
1795                                 }
1796                                 else
1797                                         kx = ky = 0;
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;
1810                                 //x -= ftbase_x;
1811                                 y -= ftbase_y;
1812
1813                                 x += thisw * dw;
1814                                 ac += 16;
1815                                 at += 8;
1816                                 av += 12;
1817                                 batchcount++;
1818                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1819                                 {
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);
1822                                         batchcount = 0;
1823                                         ac = color4f;
1824                                         at = texcoord2f;
1825                                         av = vertex3f;
1826                                 }
1827
1828                                 //prevmap = map;
1829                                 prevch = ch;
1830                         }
1831 out:
1832                         if (shadow)
1833                         {
1834                                 x -= 1.0/pix_x * r_textshadow.value;
1835                                 y -= 1.0/pix_y * r_textshadow.value;
1836                         }
1837                 }
1838         }
1839         if (batchcount > 0)
1840         {
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);
1843         }
1844
1845         if (outcolor)
1846                 *outcolor = colorindex;
1847         
1848         // note: this relies on the proper text (not shadow) being drawn last
1849         return x;
1850 }
1851
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)
1853 {
1854         return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1855 }
1856
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)
1858 {
1859         return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1860 }
1861
1862 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1863 {
1864         return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1865 }
1866
1867 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1868 {
1869         return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1870 }
1871
1872 #if 0
1873 // not used
1874 // no ^xrgb management
1875 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1876 {
1877         int color, numchars = 0;
1878         char *outputend2c = output2c + maxoutchars - 2;
1879         if (!outcolor || *outcolor == -1)
1880                 color = STRING_COLOR_DEFAULT;
1881         else
1882                 color = *outcolor;
1883         if (!maxreadchars)
1884                 maxreadchars = 1<<30;
1885         textend = text + maxreadchars;
1886         while (text != textend && *text)
1887         {
1888                 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1889                 {
1890                         if (text[1] == STRING_COLOR_TAG)
1891                                 text++;
1892                         else if (text[1] >= '0' && text[1] <= '9')
1893                         {
1894                                 color = text[1] - '0';
1895                                 text += 2;
1896                                 continue;
1897                         }
1898                 }
1899                 if (output2c >= outputend2c)
1900                         break;
1901                 *output2c++ = *text++;
1902                 *output2c++ = color;
1903                 numchars++;
1904         }
1905         output2c[0] = output2c[1] = 0;
1906         if (outcolor)
1907                 *outcolor = color;
1908         return numchars;
1909 }
1910 #endif
1911
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)
1913 {
1914         float floats[36];
1915
1916         _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1917         if(!r_draw2d.integer && !r_draw2d_force)
1918                 return;
1919
1920 //      R_Mesh_ResetTextureState();
1921         if (pic)
1922         {
1923                 if (width == 0)
1924                         width = pic->width;
1925                 if (height == 0)
1926                         height = pic->height;
1927                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1928         }
1929         else
1930                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1931
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;
1945
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);
1948 }
1949
1950 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1951 {
1952         _DrawQ_Setup();
1953         CHECKGLERROR
1954         if(!r_draw2d.integer && !r_draw2d_force)
1955                 return;
1956         DrawQ_ProcessDrawFlag(flags, hasalpha);
1957
1958 //      R_Mesh_ResetTextureState();
1959         R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1960
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);
1963 }
1964
1965 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1966 {
1967         int num;
1968
1969         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1970         if(!r_draw2d.integer && !r_draw2d_force)
1971                 return;
1972
1973         GL_Color(1,1,1,1);
1974         switch(vid.renderpath)
1975         {
1976         case RENDERPATH_GL11:
1977         case RENDERPATH_GL13:
1978         case RENDERPATH_GL20:
1979 #ifndef USE_GLES2
1980                 CHECKGLERROR
1981                 qglBegin(GL_LINE_LOOP);
1982                 for (num = 0;num < mesh->num_vertices;num++)
1983                 {
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]);
1987                 }
1988                 qglEnd();
1989                 CHECKGLERROR
1990 #endif
1991                 break;
1992         case RENDERPATH_D3D9:
1993                 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1994                 break;
1995         case RENDERPATH_D3D10:
1996                 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1997                 break;
1998         case RENDERPATH_D3D11:
1999                 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2000                 break;
2001         case RENDERPATH_SOFT:
2002                 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2003                 break;
2004         case RENDERPATH_GLES1:
2005         case RENDERPATH_GLES2:
2006                 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2007                 return;
2008         }
2009 }
2010
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)
2013 {
2014         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
2015         if(!r_draw2d.integer && !r_draw2d_force)
2016                 return;
2017
2018         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
2019
2020         switch(vid.renderpath)
2021         {
2022         case RENDERPATH_GL11:
2023         case RENDERPATH_GL13:
2024         case RENDERPATH_GL20:
2025 #ifndef USE_GLES2
2026                 CHECKGLERROR
2027
2028                 //qglLineWidth(width);CHECKGLERROR
2029
2030                 GL_Color(r,g,b,alpha);
2031                 CHECKGLERROR
2032                 qglBegin(GL_LINES);
2033                 qglVertex2f(x1, y1);
2034                 qglVertex2f(x2, y2);
2035                 qglEnd();
2036                 CHECKGLERROR
2037 #endif
2038                 break;
2039         case RENDERPATH_D3D9:
2040                 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2041                 break;
2042         case RENDERPATH_D3D10:
2043                 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2044                 break;
2045         case RENDERPATH_D3D11:
2046                 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2047                 break;
2048         case RENDERPATH_SOFT:
2049                 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2050                 break;
2051         case RENDERPATH_GLES1:
2052         case RENDERPATH_GLES2:
2053                 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2054                 return;
2055         }
2056 }
2057
2058 void DrawQ_Lines (float width, int numlines, const float *vertex3f, const float *color4f, int flags)
2059 {
2060         int i;
2061         qboolean hasalpha = false;
2062         for (i = 0;i < numlines*2;i++)
2063                 if (color4f[i*4+3] < 1.0f)
2064                         hasalpha = true;
2065
2066         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, hasalpha ? 0.5f : 1.0f);
2067
2068         if(!r_draw2d.integer && !r_draw2d_force)
2069                 return;
2070
2071         switch(vid.renderpath)
2072         {
2073         case RENDERPATH_GL11:
2074         case RENDERPATH_GL13:
2075         case RENDERPATH_GL20:
2076                 CHECKGLERROR
2077
2078                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
2079
2080                 //qglLineWidth(width);CHECKGLERROR
2081
2082                 CHECKGLERROR
2083                 R_Mesh_PrepareVertices_Generic_Arrays(numlines*2, vertex3f, color4f, NULL);
2084                 qglDrawArrays(GL_LINES, 0, numlines*2);
2085                 CHECKGLERROR
2086                 break;
2087         case RENDERPATH_D3D9:
2088                 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2089                 break;
2090         case RENDERPATH_D3D10:
2091                 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2092                 break;
2093         case RENDERPATH_D3D11:
2094                 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2095                 break;
2096         case RENDERPATH_SOFT:
2097                 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2098                 break;
2099         case RENDERPATH_GLES1:
2100         case RENDERPATH_GLES2:
2101                 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2102                 return;
2103         }
2104 }
2105
2106 void DrawQ_SetClipArea(float x, float y, float width, float height)
2107 {
2108         int ix, iy, iw, ih;
2109         _DrawQ_Setup();
2110
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)
2118         {
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);
2126                 break;
2127         case RENDERPATH_D3D9:
2128                 GL_Scissor(ix, iy, iw, ih);
2129                 break;
2130         case RENDERPATH_D3D10:
2131                 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2132                 break;
2133         case RENDERPATH_D3D11:
2134                 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2135                 break;
2136         }
2137
2138         GL_ScissorTest(true);
2139 }
2140
2141 void DrawQ_ResetClipArea(void)
2142 {
2143         _DrawQ_Setup();
2144         GL_ScissorTest(false);
2145 }
2146
2147 void DrawQ_Finish(void)
2148 {
2149         r_refdef.draw2dstage = 0;
2150 }
2151
2152 void DrawQ_RecalcView(void)
2153 {
2154         if(r_refdef.draw2dstage)
2155                 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2156 }
2157
2158 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2159 void R_DrawGamma(void)
2160 {
2161         float c[4];
2162         switch(vid.renderpath)
2163         {
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)
2170                         return;
2171                 break;
2172         case RENDERPATH_GL11:
2173         case RENDERPATH_GL13:
2174                 if (vid_usinghwgamma)
2175                         return;
2176                 break;
2177         case RENDERPATH_GLES1:
2178         case RENDERPATH_SOFT:
2179                 return;
2180         }
2181         // all the blends ignore depth
2182 //      R_Mesh_ResetTextureState();
2183         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, true, true);
2184         GL_DepthMask(true);
2185         GL_DepthRange(0, 1);
2186         GL_PolygonOffset(0, 0);
2187         GL_DepthTest(false);
2188
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
2193
2194         // apply pre-brightness (subtractive brightness, for where contrast was >= 1)
2195         if (vid.support.ext_blend_subtract)
2196         {
2197                 if (v_color_enable.integer)
2198                 {
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;
2202                 }
2203                 else
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)
2206                 {
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);
2214                 }
2215         }
2216
2217         // apply contrast
2218         if (v_color_enable.integer)
2219         {
2220                 c[0] = v_color_white_r.value;
2221                 c[1] = v_color_white_g.value;
2222                 c[2] = v_color_white_b.value;
2223         }
2224         else
2225                 c[0] = c[1] = c[2] = v_contrast.value;
2226         if (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2227         {
2228                 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2229                 while (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2230                 {
2231                         float cc[4];
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);
2238                         c[0] /= 1 + cc[0];
2239                         c[1] /= 1 + cc[1];
2240                         c[2] /= 1 + cc[2];
2241                 }
2242         }
2243         if (c[0] <= 0.997f || c[1] <= 0.997f || c[2] <= 0.997f)
2244         {
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);
2249         }
2250
2251         // apply post-brightness (additive brightness, for where contrast was <= 1)
2252         if (v_color_enable.integer)
2253         {
2254                 c[0] = v_color_black_r.value;
2255                 c[1] = v_color_black_g.value;
2256                 c[2] = v_color_black_b.value;
2257         }
2258         else
2259                 c[0] = c[1] = c[2] = v_brightness.value;
2260         if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2261         {
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);
2266         }
2267 }
2268