]> git.xonotic.org Git - xonotic/darkplaces.git/blob - gl_draw.c
fix typos
[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         strlcpy (pic->name, path, sizeof(pic->name));
365         // link into list
366         pic->chain = cachepichash[hashkey];
367         cachepichash[hashkey] = pic;
368
369 reload:
370         // check whether it is an dynamic texture (if so, we can directly use its texture handler)
371         pic->flags = cachepicflags;
372         pic->tex = CL_GetDynTexture( path );
373         // if so, set the width/height, too
374         if( pic->tex ) {
375                 pic->width = R_TextureWidth(pic->tex);
376                 pic->height = R_TextureHeight(pic->tex);
377                 // we're done now (early-out)
378                 return pic;
379         }
380
381         pic->hasalpha = true; // assume alpha unless we know it has none
382         pic->texflags = texflags;
383         pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
384         pic->lastusedframe = draw_frame;
385
386         // load a high quality image from disk if possible
387         if (!loaded && r_texture_dds_load.integer != 0 && (pic->tex = R_LoadTextureDDSFile(drawtexturepool, va("dds/%s.dds", pic->name), pic->texflags, &ddshasalpha, ddsavgcolor, 0)))
388         {
389                 // note this loads even if autoload is true, otherwise we can't get the width/height
390                 loaded = true;
391                 pic->hasalpha = ddshasalpha;
392                 pic->width = R_TextureWidth(pic->tex);
393                 pic->height = R_TextureHeight(pic->tex);
394         }
395         if (!loaded && ((pixels = loadimagepixelsbgra(pic->name, false, true, false, NULL)) || (!strncmp(pic->name, "gfx/", 4) && (pixels = loadimagepixelsbgra(pic->name+4, false, true, false, NULL)))))
396         {
397                 loaded = true;
398                 pic->hasalpha = false;
399                 if (pic->texflags & TEXF_ALPHA)
400                 {
401                         for (j = 3;j < image_width * image_height * 4;j += 4)
402                         {
403                                 if (pixels[j] < 255)
404                                 {
405                                         pic->hasalpha = true;
406                                         break;
407                                 }
408                         }
409                 }
410
411                 pic->width = image_width;
412                 pic->height = image_height;
413                 if (!pic->autoload)
414                 {
415                         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);
416 #ifndef USE_GLES2
417                         if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
418                                 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
419 #endif
420                 }
421         }
422         if (!loaded)
423         {
424                 pic->autoload = false;
425                 // never compress the fallback images
426                 pic->texflags &= ~TEXF_COMPRESS;
427         }
428
429         // now read the low quality version (wad or lmp file), and take the pic
430         // size from that even if we don't upload the texture, this way the pics
431         // show up the right size in the menu even if they were replaced with
432         // higher or lower resolution versions
433         dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", pic->name);
434         if (!strncmp(pic->name, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
435         {
436                 if (developer_loading.integer)
437                         Con_Printf("loading lump \"%s\"\n", pic->name);
438
439                 if (lmpsize >= 9)
440                 {
441                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
442                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
443                         // if no high quality replacement image was found, upload the original low quality texture
444                         if (!loaded)
445                         {
446                                 loaded = true;
447                                 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);
448                         }
449                 }
450                 Mem_Free(lmpdata);
451         }
452         else if ((lmpdata = W_GetLumpName (pic->name + 4)))
453         {
454                 if (developer_loading.integer)
455                         Con_Printf("loading gfx.wad lump \"%s\"\n", pic->name + 4);
456
457                 if (!strcmp(pic->name, "gfx/conchars"))
458                 {
459                         // conchars is a raw image and with color 0 as transparent instead of 255
460                         pic->width = 128;
461                         pic->height = 128;
462                         // if no high quality replacement image was found, upload the original low quality texture
463                         if (!loaded)
464                         {
465                                 loaded = true;
466                                 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, 128, 128, lmpdata, vid.sRGB2D != 0 ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
467                         }
468                 }
469                 else
470                 {
471                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
472                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
473                         // if no high quality replacement image was found, upload the original low quality texture
474                         if (!loaded)
475                         {
476                                 loaded = true;
477                                 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);
478                         }
479                 }
480         }
481
482         if (pixels)
483         {
484                 Mem_Free(pixels);
485                 pixels = NULL;
486         }
487         if (!loaded)
488         {
489                 // if it's not found on disk, generate an image
490                 pic->tex = draw_generatepic(pic->name, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
491                 pic->width = R_TextureWidth(pic->tex);
492                 pic->height = R_TextureHeight(pic->tex);
493         }
494
495         return pic;
496 }
497
498 cachepic_t *Draw_CachePic (const char *path)
499 {
500         return Draw_CachePic_Flags (path, 0); // default to persistent!
501 }
502
503 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
504 {
505         if (pic->autoload && !pic->tex)
506         {
507                 if (pic->tex == NULL && r_texture_dds_load.integer != 0)
508                 {
509                         qboolean ddshasalpha;
510                         float ddsavgcolor[4];
511                         pic->tex = R_LoadTextureDDSFile(drawtexturepool, va("dds/%s.dds", pic->name), pic->texflags, &ddshasalpha, ddsavgcolor, 0);
512                 }
513                 if (pic->tex == NULL)
514                 {
515                         pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, vid.sRGB2D);
516 #ifndef USE_GLES2
517                         if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
518                                 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
519 #endif
520                 }
521                 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
522                 {
523                         pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, vid.sRGB2D);
524 #ifndef USE_GLES2
525                         if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
526                                 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
527 #endif
528                 }
529                 if (pic->tex == NULL)
530                         pic->tex = draw_generatepic(pic->name, true);
531         }
532         pic->lastusedframe = draw_frame;
533         return pic->tex;
534 }
535
536 void Draw_Frame(void)
537 {
538         int i;
539         cachepic_t *pic;
540         static double nextpurgetime;
541         if (nextpurgetime > realtime)
542                 return;
543         nextpurgetime = realtime + 0.05;
544         for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
545         {
546                 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
547                 {
548                         R_FreeTexture(pic->tex);
549                         pic->tex = NULL;
550                 }
551         }
552         draw_frame++;
553 }
554
555 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
556 {
557         int crc, hashkey;
558         cachepic_t *pic;
559
560         crc = CRC_Block((unsigned char *)picname, strlen(picname));
561         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
562         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
563                 if (!strcmp (picname, pic->name))
564                         break;
565
566         if (pic)
567         {
568                 if (pic->flags == CACHEPICFLAG_NEWPIC && pic->tex && pic->width == width && pic->height == height)
569                 {
570                         R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, 0, width, height, 1);
571                         return pic;
572                 }
573         }
574         else
575         {
576                 if (pic == NULL)
577                 {
578                         if (numcachepics == MAX_CACHED_PICS)
579                         {
580                                 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
581                                 // FIXME: support NULL in callers?
582                                 return cachepics; // return the first one
583                         }
584                         pic = cachepics + (numcachepics++);
585                         strlcpy (pic->name, picname, sizeof(pic->name));
586                         // link into list
587                         pic->chain = cachepichash[hashkey];
588                         cachepichash[hashkey] = pic;
589                 }
590         }
591
592         pic->flags = CACHEPICFLAG_NEWPIC; // disable texflags checks in Draw_CachePic
593         pic->width = width;
594         pic->height = height;
595         if (pic->tex)
596                 R_FreeTexture(pic->tex);
597         pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0), -1, NULL);
598         return pic;
599 }
600
601 void Draw_FreePic(const char *picname)
602 {
603         int crc;
604         int hashkey;
605         cachepic_t *pic;
606         // this doesn't really free the pic, but does free it's texture
607         crc = CRC_Block((unsigned char *)picname, strlen(picname));
608         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
609         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
610         {
611                 if (!strcmp (picname, pic->name) && pic->tex)
612                 {
613                         R_FreeTexture(pic->tex);
614                         pic->tex = NULL;
615                         pic->width = 0;
616                         pic->height = 0;
617                         return;
618                 }
619         }
620 }
621
622 static float snap_to_pixel_x(float x, float roundUpAt);
623 extern int con_linewidth; // to force rewrapping
624 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
625 {
626         int i, ch;
627         float maxwidth;
628         char widthfile[MAX_QPATH];
629         char *widthbuf;
630         fs_offset_t widthbufsize;
631
632         if(override || !fnt->texpath[0])
633         {
634                 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
635                 // load the cvars when the font is FIRST loader
636                 fnt->settings.scale = scale;
637                 fnt->settings.voffset = voffset;
638                 fnt->settings.antialias = r_font_antialias.integer;
639                 fnt->settings.hinting = r_font_hinting.integer;
640                 fnt->settings.outline = r_font_postprocess_outline.value;
641                 fnt->settings.blur = r_font_postprocess_blur.value;
642                 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
643                 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
644                 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
645         }
646         // fix bad scale
647         if (fnt->settings.scale <= 0)
648                 fnt->settings.scale = 1;
649
650         if(drawtexturepool == NULL)
651                 return; // before gl_draw_start, so will be loaded later
652
653         if(fnt->ft2)
654         {
655                 // clear freetype font
656                 Font_UnloadFont(fnt->ft2);
657                 Mem_Free(fnt->ft2);
658                 fnt->ft2 = NULL;
659         }
660
661         if(fnt->req_face != -1)
662         {
663                 if(!Font_LoadFont(fnt->texpath, fnt))
664                         Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
665         }
666
667         fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
668         if(fnt->tex == r_texture_notexture)
669         {
670                 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
671                 {
672                         if (!fnt->fallbacks[i][0])
673                                 break;
674                         fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
675                         if(fnt->tex != r_texture_notexture)
676                                 break;
677                 }
678                 if(fnt->tex == r_texture_notexture)
679                 {
680                         fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
681                         strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
682                 }
683                 else
684                         dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
685         }
686         else
687                 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
688
689         // unspecified width == 1 (base width)
690         for(ch = 0; ch < 256; ++ch)
691                 fnt->width_of[ch] = 1;
692
693         // FIXME load "name.width", if it fails, fill all with 1
694         if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
695         {
696                 float extraspacing = 0;
697                 const char *p = widthbuf;
698
699                 ch = 0;
700                 while(ch < 256)
701                 {
702                         if(!COM_ParseToken_Simple(&p, false, false))
703                                 return;
704
705                         switch(*com_token)
706                         {
707                                 case '0':
708                                 case '1':
709                                 case '2':
710                                 case '3':
711                                 case '4':
712                                 case '5':
713                                 case '6':
714                                 case '7':
715                                 case '8':
716                                 case '9':
717                                 case '+':
718                                 case '-':
719                                 case '.':
720                                         fnt->width_of[ch] = atof(com_token) + extraspacing;
721                                         ch++;
722                                         break;
723                                 default:
724                                         if(!strcmp(com_token, "extraspacing"))
725                                         {
726                                                 if(!COM_ParseToken_Simple(&p, false, false))
727                                                         return;
728                                                 extraspacing = atof(com_token);
729                                         }
730                                         else if(!strcmp(com_token, "scale"))
731                                         {
732                                                 if(!COM_ParseToken_Simple(&p, false, false))
733                                                         return;
734                                                 fnt->settings.scale = atof(com_token);
735                                         }
736                                         else
737                                         {
738                                                 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
739                                                 if(!COM_ParseToken_Simple(&p, false, false))
740                                                         return;
741                                         }
742                                         break;
743                         }
744                 }
745
746                 Mem_Free(widthbuf);
747         }
748
749         if(fnt->ft2)
750         {
751                 for (i = 0; i < MAX_FONT_SIZES; ++i)
752                 {
753                         ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
754                         if (!map)
755                                 break;
756                         for(ch = 0; ch < 256; ++ch)
757                                 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
758                 }
759         }
760
761         maxwidth = fnt->width_of[0];
762         for(i = 1; i < 256; ++i)
763                 maxwidth = max(maxwidth, fnt->width_of[i]);
764         fnt->maxwidth = maxwidth;
765
766         // fix up maxwidth for overlap
767         fnt->maxwidth *= fnt->settings.scale;
768
769         if(fnt == FONT_CONSOLE)
770                 con_linewidth = -1; // rewrap console in next frame
771 }
772
773 extern cvar_t developer_font;
774 dp_font_t *FindFont(const char *title, qboolean allocate_new)
775 {
776         int i, oldsize;
777
778         // find font
779         for(i = 0; i < dp_fonts.maxsize; ++i)
780                 if(!strcmp(dp_fonts.f[i].title, title))
781                         return &dp_fonts.f[i];
782         // if not found - try allocate
783         if (allocate_new)
784         {
785                 // find any font with empty title
786                 for(i = 0; i < dp_fonts.maxsize; ++i)
787                 {
788                         if(!strcmp(dp_fonts.f[i].title, ""))
789                         {
790                                 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
791                                 return &dp_fonts.f[i];
792                         }
793                 }
794                 // if no any 'free' fonts - expand buffer
795                 oldsize = dp_fonts.maxsize;
796                 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
797                 if (developer_font.integer)
798                         Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", oldsize, dp_fonts.maxsize);
799                 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
800                 // relink ft2 structures
801                 for(i = 0; i < oldsize; ++i)
802                         if (dp_fonts.f[i].ft2)
803                                 dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings;
804                 // register a font in first expanded slot
805                 strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
806                 return &dp_fonts.f[oldsize];
807         }
808         return NULL;
809 }
810
811 static float snap_to_pixel_x(float x, float roundUpAt)
812 {
813         float pixelpos = x * vid.width / vid_conwidth.value;
814         int snap = (int) pixelpos;
815         if (pixelpos - snap >= roundUpAt) ++snap;
816         return ((float)snap * vid_conwidth.value / vid.width);
817         /*
818         x = (int)(x * vid.width / vid_conwidth.value);
819         x = (x * vid_conwidth.value / vid.width);
820         return x;
821         */
822 }
823
824 static float snap_to_pixel_y(float y, float roundUpAt)
825 {
826         float pixelpos = y * vid.height / vid_conheight.value;
827         int snap = (int) pixelpos;
828         if (pixelpos - snap > roundUpAt) ++snap;
829         return ((float)snap * vid_conheight.value / vid.height);
830         /*
831         y = (int)(y * vid.height / vid_conheight.value);
832         y = (y * vid_conheight.value / vid.height);
833         return y;
834         */
835 }
836
837 static void LoadFont_f(void)
838 {
839         dp_font_t *f;
840         int i, sizes;
841         const char *filelist, *c, *cm;
842         float sz, scale, voffset;
843         char mainfont[MAX_QPATH];
844
845         if(Cmd_Argc() < 2)
846         {
847                 Con_Printf("Available font commands:\n");
848                 for(i = 0; i < dp_fonts.maxsize; ++i)
849                         if (dp_fonts.f[i].title[0])
850                                 Con_Printf("  loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
851                 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
852                            "can specify multiple fonts and faces\n"
853                            "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
854                            "to load face 2 of the font gfx/vera-sans and use face 1\n"
855                            "of gfx/fallback as fallback font.\n"
856                            "You can also specify a list of font sizes to load, like this:\n"
857                            "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
858                            "In many cases, 8 12 16 24 32 should be a good choice.\n"
859                            "custom switches:\n"
860                            " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
861                            " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
862                         );
863                 return;
864         }
865         f = FindFont(Cmd_Argv(1), true);
866         if(f == NULL)
867         {
868                 Con_Printf("font function not found\n");
869                 return;
870         }
871
872         if(Cmd_Argc() < 3)
873                 filelist = "gfx/conchars";
874         else
875                 filelist = Cmd_Argv(2);
876
877         memset(f->fallbacks, 0, sizeof(f->fallbacks));
878         memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
879
880         // first font is handled "normally"
881         c = strchr(filelist, ':');
882         cm = strchr(filelist, ',');
883         if(c && (!cm || c < cm))
884                 f->req_face = atoi(c+1);
885         else
886         {
887                 f->req_face = 0;
888                 c = cm;
889         }
890
891         if(!c || (c - filelist) > MAX_QPATH)
892                 strlcpy(mainfont, filelist, sizeof(mainfont));
893         else
894         {
895                 memcpy(mainfont, filelist, c - filelist);
896                 mainfont[c - filelist] = 0;
897         }
898
899         for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
900         {
901                 c = strchr(filelist, ',');
902                 if(!c)
903                         break;
904                 filelist = c + 1;
905                 if(!*filelist)
906                         break;
907                 c = strchr(filelist, ':');
908                 cm = strchr(filelist, ',');
909                 if(c && (!cm || c < cm))
910                         f->fallback_faces[i] = atoi(c+1);
911                 else
912                 {
913                         f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
914                         c = cm;
915                 }
916                 if(!c || (c-filelist) > MAX_QPATH)
917                 {
918                         strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
919                 }
920                 else
921                 {
922                         memcpy(f->fallbacks[i], filelist, c - filelist);
923                         f->fallbacks[i][c - filelist] = 0;
924                 }
925         }
926
927         // for now: by default load only one size: the default size
928         f->req_sizes[0] = 0;
929         for(i = 1; i < MAX_FONT_SIZES; ++i)
930                 f->req_sizes[i] = -1;
931
932         scale = 1;
933         voffset = 0;
934         if(Cmd_Argc() >= 4)
935         {
936                 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
937                 {
938                         // special switches
939                         if (!strcmp(Cmd_Argv(i), "scale"))
940                         {
941                                 i++;
942                                 if (i < Cmd_Argc())
943                                         scale = atof(Cmd_Argv(i));
944                                 continue;
945                         }
946                         if (!strcmp(Cmd_Argv(i), "voffset"))
947                         {
948                                 i++;
949                                 if (i < Cmd_Argc())
950                                         voffset = atof(Cmd_Argv(i));
951                                 continue;
952                         }
953
954                         if (sizes == -1)
955                                 continue; // no slot for other sizes
956
957                         // parse one of sizes
958                         sz = atof(Cmd_Argv(i));
959                         if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
960                         {
961                                 // search for duplicated sizes
962                                 int j;
963                                 for (j=0; j<sizes; j++)
964                                         if (f->req_sizes[j] == sz)
965                                                 break;
966                                 if (j != sizes)
967                                         continue; // sz already in req_sizes, don't add it again
968
969                                 if (sizes == MAX_FONT_SIZES)
970                                 {
971                                         Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
972                                         sizes = -1;
973                                         continue;
974                                 }
975                                 f->req_sizes[sizes] = sz;
976                                 sizes++;
977                         }
978                 }
979         }
980
981         LoadFont(true, mainfont, f, scale, voffset);
982 }
983
984 /*
985 ===============
986 Draw_Init
987 ===============
988 */
989 static void gl_draw_start(void)
990 {
991         int i;
992         drawtexturepool = R_AllocTexturePool();
993
994         numcachepics = 0;
995         memset(cachepichash, 0, sizeof(cachepichash));
996
997         font_start();
998
999         // load default font textures
1000         for(i = 0; i < dp_fonts.maxsize; ++i)
1001                 if (dp_fonts.f[i].title[0])
1002                         LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
1003
1004         // draw the loading screen so people have something to see in the newly opened window
1005         SCR_UpdateLoadingScreen(true);
1006 }
1007
1008 static void gl_draw_shutdown(void)
1009 {
1010         font_shutdown();
1011
1012         R_FreeTexturePool(&drawtexturepool);
1013
1014         numcachepics = 0;
1015         memset(cachepichash, 0, sizeof(cachepichash));
1016 }
1017
1018 static void gl_draw_newmap(void)
1019 {
1020         font_newmap();
1021 }
1022
1023 void GL_Draw_Init (void)
1024 {
1025         int i, j;
1026
1027         Cvar_RegisterVariable(&r_font_postprocess_blur);
1028         Cvar_RegisterVariable(&r_font_postprocess_outline);
1029         Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
1030         Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
1031         Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
1032         Cvar_RegisterVariable(&r_font_hinting);
1033         Cvar_RegisterVariable(&r_font_antialias);
1034         Cvar_RegisterVariable(&r_textshadow);
1035         Cvar_RegisterVariable(&r_textbrightness);
1036         Cvar_RegisterVariable(&r_textcontrast);
1037
1038         // allocate fonts storage
1039         fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
1040         dp_fonts.maxsize = MAX_FONTS;
1041         dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
1042         memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
1043
1044         // assign starting font names
1045         strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
1046         strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
1047         strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
1048         strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
1049         strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
1050         strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
1051         strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
1052         strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
1053         strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
1054         for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
1055                 if(!FONT_USER(i)->title[0])
1056                         dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
1057
1058         Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
1059         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
1060 }
1061
1062 static void _DrawQ_Setup(void)
1063 {
1064         r_viewport_t viewport;
1065         if (r_refdef.draw2dstage == 1)
1066                 return;
1067         r_refdef.draw2dstage = 1;
1068         CHECKGLERROR
1069         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);
1070         R_Mesh_ResetRenderTargets();
1071         R_SetViewport(&viewport);
1072         GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
1073         GL_DepthFunc(GL_LEQUAL);
1074         GL_PolygonOffset(0,0);
1075         GL_CullFace(GL_NONE);
1076         R_EntityMatrix(&identitymatrix);
1077
1078         GL_DepthRange(0, 1);
1079         GL_PolygonOffset(0, 0);
1080         GL_DepthTest(false);
1081         GL_Color(1,1,1,1);
1082 }
1083
1084 qboolean r_draw2d_force = false;
1085 void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1086 {
1087         _DrawQ_Setup();
1088         CHECKGLERROR
1089         if(!r_draw2d.integer && !r_draw2d_force)
1090                 return;
1091         DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1092 }
1093 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1094 {
1095         if(flags == DRAWFLAG_ADDITIVE)
1096         {
1097                 GL_DepthMask(false);
1098                 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1099         }
1100         else if(flags == DRAWFLAG_MODULATE)
1101         {
1102                 GL_DepthMask(false);
1103                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1104         }
1105         else if(flags == DRAWFLAG_2XMODULATE)
1106         {
1107                 GL_DepthMask(false);
1108                 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1109         }
1110         else if(flags == DRAWFLAG_SCREEN)
1111         {
1112                 GL_DepthMask(false);
1113                 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1114         }
1115         else if(alpha)
1116         {
1117                 GL_DepthMask(false);
1118                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1119         }
1120         else
1121         {
1122                 GL_DepthMask(true);
1123                 GL_BlendFunc(GL_ONE, GL_ZERO);
1124         }
1125 }
1126
1127 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1128 {
1129         float floats[36];
1130
1131         _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1132         if(!r_draw2d.integer && !r_draw2d_force)
1133                 return;
1134
1135 //      R_Mesh_ResetTextureState();
1136         floats[12] = 0.0f;floats[13] = 0.0f;
1137         floats[14] = 1.0f;floats[15] = 0.0f;
1138         floats[16] = 1.0f;floats[17] = 1.0f;
1139         floats[18] = 0.0f;floats[19] = 1.0f;
1140         floats[20] = floats[24] = floats[28] = floats[32] = red;
1141         floats[21] = floats[25] = floats[29] = floats[33] = green;
1142         floats[22] = floats[26] = floats[30] = floats[34] = blue;
1143         floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1144         if (pic)
1145         {
1146                 if (width == 0)
1147                         width = pic->width;
1148                 if (height == 0)
1149                         height = pic->height;
1150                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1151
1152 #if 0
1153       // AK07: lets be texel correct on the corners
1154       {
1155          float horz_offset = 0.5f / pic->width;
1156          float vert_offset = 0.5f / pic->height;
1157
1158                    floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1159                    floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1160                    floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1161                    floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1162       }
1163 #endif
1164         }
1165         else
1166                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1167
1168         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1169         floats[0] = floats[9] = x;
1170         floats[1] = floats[4] = y;
1171         floats[3] = floats[6] = x + width;
1172         floats[7] = floats[10] = y + height;
1173
1174         R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1175         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1176 }
1177
1178 void DrawQ_RotPic(float x, float y, cachepic_t *pic, float width, float height, float org_x, float org_y, float angle, float red, float green, float blue, float alpha, int flags)
1179 {
1180         float floats[36];
1181         float af = DEG2RAD(-angle); // forward
1182         float ar = DEG2RAD(-angle + 90); // right
1183         float sinaf = sin(af);
1184         float cosaf = cos(af);
1185         float sinar = sin(ar);
1186         float cosar = cos(ar);
1187
1188         _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1189         if(!r_draw2d.integer && !r_draw2d_force)
1190                 return;
1191
1192 //      R_Mesh_ResetTextureState();
1193         if (pic)
1194         {
1195                 if (width == 0)
1196                         width = pic->width;
1197                 if (height == 0)
1198                         height = pic->height;
1199                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1200         }
1201         else
1202                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1203
1204         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1205
1206 // top left
1207         floats[0] = x - cosaf*org_x - cosar*org_y;
1208         floats[1] = y - sinaf*org_x - sinar*org_y;
1209
1210 // top right
1211         floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1212         floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1213
1214 // bottom right
1215         floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1216         floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1217
1218 // bottom left
1219         floats[9]  = x - cosaf*org_x + cosar*(height-org_y);
1220         floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1221
1222         floats[12] = 0.0f;floats[13] = 0.0f;
1223         floats[14] = 1.0f;floats[15] = 0.0f;
1224         floats[16] = 1.0f;floats[17] = 1.0f;
1225         floats[18] = 0.0f;floats[19] = 1.0f;
1226         floats[20] = floats[24] = floats[28] = floats[32] = red;
1227         floats[21] = floats[25] = floats[29] = floats[33] = green;
1228         floats[22] = floats[26] = floats[30] = floats[34] = blue;
1229         floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1230
1231         R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1232         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1233 }
1234
1235 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1236 {
1237         float floats[36];
1238
1239         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1240         if(!r_draw2d.integer && !r_draw2d_force)
1241                 return;
1242
1243 //      R_Mesh_ResetTextureState();
1244         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1245
1246         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1247         floats[0] = floats[9] = x;
1248         floats[1] = floats[4] = y;
1249         floats[3] = floats[6] = x + width;
1250         floats[7] = floats[10] = y + height;
1251         floats[12] = 0.0f;floats[13] = 0.0f;
1252         floats[14] = 1.0f;floats[15] = 0.0f;
1253         floats[16] = 1.0f;floats[17] = 1.0f;
1254         floats[18] = 0.0f;floats[19] = 1.0f;
1255         floats[20] = floats[24] = floats[28] = floats[32] = red;
1256         floats[21] = floats[25] = floats[29] = floats[33] = green;
1257         floats[22] = floats[26] = floats[30] = floats[34] = blue;
1258         floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1259
1260         R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1261         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1262 }
1263
1264 /// color tag printing
1265 static const vec4_t string_colors[] =
1266 {
1267         // Quake3 colors
1268         // LordHavoc: why on earth is cyan before magenta in Quake3?
1269         // LordHavoc: note: Doom3 uses white for [0] and [7]
1270         {0.0, 0.0, 0.0, 1.0}, // black
1271         {1.0, 0.0, 0.0, 1.0}, // red
1272         {0.0, 1.0, 0.0, 1.0}, // green
1273         {1.0, 1.0, 0.0, 1.0}, // yellow
1274         {0.0, 0.0, 1.0, 1.0}, // blue
1275         {0.0, 1.0, 1.0, 1.0}, // cyan
1276         {1.0, 0.0, 1.0, 1.0}, // magenta
1277         {1.0, 1.0, 1.0, 1.0}, // white
1278         // [515]'s BX_COLOREDTEXT extension
1279         {1.0, 1.0, 1.0, 0.5}, // half transparent
1280         {0.5, 0.5, 0.5, 1.0}  // half brightness
1281         // Black's color table
1282         //{1.0, 1.0, 1.0, 1.0},
1283         //{1.0, 0.0, 0.0, 1.0},
1284         //{0.0, 1.0, 0.0, 1.0},
1285         //{0.0, 0.0, 1.0, 1.0},
1286         //{1.0, 1.0, 0.0, 1.0},
1287         //{0.0, 1.0, 1.0, 1.0},
1288         //{1.0, 0.0, 1.0, 1.0},
1289         //{0.1, 0.1, 0.1, 1.0}
1290 };
1291
1292 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
1293
1294 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1295 {
1296         float C = r_textcontrast.value;
1297         float B = r_textbrightness.value;
1298         if (colorindex & 0x10000) // that bit means RGB color
1299         {
1300                 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1301                 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1302                 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1303                 color[3] = (colorindex & 0xf) / 15.0;
1304         }
1305         else
1306                 Vector4Copy(string_colors[colorindex], color);
1307         Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1308         if (shadow)
1309         {
1310                 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1311                 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1312         }
1313 }
1314
1315 // NOTE: this function always draws exactly one character if maxwidth <= 0
1316 float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *maxlen, float w, float h, float sw, float sh, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
1317 {
1318         const char *text_start = text;
1319         int colorindex = STRING_COLOR_DEFAULT;
1320         size_t i;
1321         float x = 0;
1322         Uchar ch, mapch, nextch;
1323         Uchar prevch = 0; // used for kerning
1324         int tempcolorindex;
1325         float kx;
1326         int map_index = 0;
1327         size_t bytes_left;
1328         ft2_font_map_t *fontmap = NULL;
1329         ft2_font_map_t *map = NULL;
1330         //ft2_font_map_t *prevmap = NULL;
1331         ft2_font_t *ft2 = fnt->ft2;
1332         // float ftbase_x;
1333         qboolean snap = true;
1334         qboolean least_one = false;
1335         float dw; // display w
1336         //float dh; // display h
1337         const float *width_of;
1338
1339         if (!h) h = w;
1340         if (!h) {
1341                 w = h = 1;
1342                 snap = false;
1343         }
1344         // do this in the end
1345         w *= fnt->settings.scale;
1346         h *= fnt->settings.scale;
1347
1348         // find the most fitting size:
1349         if (ft2 != NULL)
1350         {
1351                 if (snap)
1352                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1353                 else
1354                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1355                 fontmap = Font_MapForIndex(ft2, map_index);
1356         }
1357
1358         dw = w * sw;
1359         //dh = h * sh;
1360
1361         if (*maxlen < 1)
1362                 *maxlen = 1<<30;
1363
1364         if (!outcolor || *outcolor == -1)
1365                 colorindex = STRING_COLOR_DEFAULT;
1366         else
1367                 colorindex = *outcolor;
1368
1369         // maxwidth /= fnt->scale; // w and h are multiplied by it already
1370         // ftbase_x = snap_to_pixel_x(0);
1371         
1372         if(maxwidth <= 0)
1373         {
1374                 least_one = true;
1375                 maxwidth = -maxwidth;
1376         }
1377
1378         //if (snap)
1379         //      x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1380
1381         if (fontmap)
1382                 width_of = fontmap->width_of;
1383         else
1384                 width_of = fnt->width_of;
1385
1386         for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1387         {
1388                 size_t i0 = i;
1389                 nextch = ch = u8_getnchar(text, &text, bytes_left);
1390                 i = text - text_start;
1391                 if (!ch)
1392                         break;
1393                 if (ch == ' ' && !fontmap)
1394                 {
1395                         if(!least_one || i0) // never skip the first character
1396                         if(x + width_of[(int) ' '] * dw > maxwidth)
1397                         {
1398                                 i = i0;
1399                                 break; // oops, can't draw this
1400                         }
1401                         x += width_of[(int) ' '] * dw;
1402                         continue;
1403                 }
1404                 // i points to the char after ^
1405                 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1406                 {
1407                         ch = *text; // colors are ascii, so no u8_ needed
1408                         if (ch <= '9' && ch >= '0') // ^[0-9] found
1409                         {
1410                                 colorindex = ch - '0';
1411                                 ++text;
1412                                 ++i;
1413                                 continue;
1414                         }
1415                         // i points to the char after ^...
1416                         // i+3 points to 3 in ^x123
1417                         // i+3 == *maxlen would mean that char is missing
1418                         else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1419                         {
1420                                 // building colorindex...
1421                                 ch = tolower(text[1]);
1422                                 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1423                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1424                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1425                                 else tempcolorindex = 0;
1426                                 if (tempcolorindex)
1427                                 {
1428                                         ch = tolower(text[2]);
1429                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1430                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1431                                         else tempcolorindex = 0;
1432                                         if (tempcolorindex)
1433                                         {
1434                                                 ch = tolower(text[3]);
1435                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1436                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1437                                                 else tempcolorindex = 0;
1438                                                 if (tempcolorindex)
1439                                                 {
1440                                                         colorindex = tempcolorindex | 0xf;
1441                                                         // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1442                                                         i+=4;
1443                                                         text += 4;
1444                                                         continue;
1445                                                 }
1446                                         }
1447                                 }
1448                         }
1449                         else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1450                         {
1451                                 i++;
1452                                 text++;
1453                         }
1454                         i--;
1455                 }
1456                 ch = nextch;
1457
1458                 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1459                 {
1460                         if (ch > 0xE000)
1461                                 ch -= 0xE000;
1462                         if (ch > 0xFF)
1463                                 continue;
1464                         if (fontmap)
1465                                 map = ft2_oldstyle_map;
1466                         prevch = 0;
1467                         if(!least_one || i0) // never skip the first character
1468                         if(x + width_of[ch] * dw > maxwidth)
1469                         {
1470                                 i = i0;
1471                                 break; // oops, can't draw this
1472                         }
1473                         x += width_of[ch] * dw;
1474                 } else {
1475                         if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1476                         {
1477                                 map = FontMap_FindForChar(fontmap, ch);
1478                                 if (!map)
1479                                 {
1480                                         if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1481                                                 break;
1482                                         if (!map)
1483                                                 break;
1484                                 }
1485                         }
1486                         mapch = ch - map->start;
1487                         if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1488                                 x += kx * dw;
1489                         x += map->glyphs[mapch].advance_x * dw;
1490                         //prevmap = map;
1491                         prevch = ch;
1492                 }
1493         }
1494
1495         *maxlen = i;
1496
1497         if (outcolor)
1498                 *outcolor = colorindex;
1499
1500         return x;
1501 }
1502
1503 float DrawQ_Color[4];
1504 float DrawQ_String_Scale(float startx, float starty, const char *text, size_t maxlen, float w, float h, float sw, float sh, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt)
1505 {
1506         int shadow, colorindex = STRING_COLOR_DEFAULT;
1507         size_t i;
1508         float x = startx, y, s, t, u, v, thisw;
1509         float *av, *at, *ac;
1510         int batchcount;
1511         static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1512         static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1513         static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1514         Uchar ch, mapch, nextch;
1515         Uchar prevch = 0; // used for kerning
1516         int tempcolorindex;
1517         int map_index = 0;
1518         //ft2_font_map_t *prevmap = NULL; // the previous map
1519         ft2_font_map_t *map = NULL;     // the currently used map
1520         ft2_font_map_t *fontmap = NULL; // the font map for the size
1521         float ftbase_y;
1522         const char *text_start = text;
1523         float kx, ky;
1524         ft2_font_t *ft2 = fnt->ft2;
1525         qboolean snap = true;
1526         float pix_x, pix_y;
1527         size_t bytes_left;
1528         float dw, dh;
1529         const float *width_of;
1530
1531         int tw, th;
1532         tw = R_TextureWidth(fnt->tex);
1533         th = R_TextureHeight(fnt->tex);
1534
1535         if (!h) h = w;
1536         if (!h) {
1537                 h = w = 1;
1538                 snap = false;
1539         }
1540
1541         starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1542         w *= fnt->settings.scale;
1543         h *= fnt->settings.scale;
1544
1545         if (ft2 != NULL)
1546         {
1547                 if (snap)
1548                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1549                 else
1550                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1551                 fontmap = Font_MapForIndex(ft2, map_index);
1552         }
1553
1554         dw = w * sw;
1555         dh = h * sh;
1556
1557         // draw the font at its baseline when using freetype
1558         //ftbase_x = 0;
1559         ftbase_y = dh * (4.5/6.0);
1560
1561         if (maxlen < 1)
1562                 maxlen = 1<<30;
1563
1564         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1565         if(!r_draw2d.integer && !r_draw2d_force)
1566                 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1567
1568 //      R_Mesh_ResetTextureState();
1569         if (!fontmap)
1570                 R_Mesh_TexBind(0, fnt->tex);
1571         R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1572
1573         ac = color4f;
1574         at = texcoord2f;
1575         av = vertex3f;
1576         batchcount = 0;
1577
1578         //ftbase_x = snap_to_pixel_x(ftbase_x);
1579         if(snap)
1580         {
1581                 startx = snap_to_pixel_x(startx, 0.4);
1582                 starty = snap_to_pixel_y(starty, 0.4);
1583                 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1584         }
1585
1586         pix_x = vid.width / vid_conwidth.value;
1587         pix_y = vid.height / vid_conheight.value;
1588
1589         if (fontmap)
1590                 width_of = fontmap->width_of;
1591         else
1592                 width_of = fnt->width_of;
1593
1594         for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1595         {
1596                 prevch = 0;
1597                 text = text_start;
1598
1599                 if (!outcolor || *outcolor == -1)
1600                         colorindex = STRING_COLOR_DEFAULT;
1601                 else
1602                         colorindex = *outcolor;
1603
1604                 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1605
1606                 x = startx;
1607                 y = starty;
1608                 /*
1609                 if (shadow)
1610                 {
1611                         x += r_textshadow.value * vid.width / vid_conwidth.value;
1612                         y += r_textshadow.value * vid.height / vid_conheight.value;
1613                 }
1614                 */
1615                 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1616                 {
1617                         nextch = ch = u8_getnchar(text, &text, bytes_left);
1618                         i = text - text_start;
1619                         if (!ch)
1620                                 break;
1621                         if (ch == ' ' && !fontmap)
1622                         {
1623                                 x += width_of[(int) ' '] * dw;
1624                                 continue;
1625                         }
1626                         if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1627                         {
1628                                 ch = *text; // colors are ascii, so no u8_ needed
1629                                 if (ch <= '9' && ch >= '0') // ^[0-9] found
1630                                 {
1631                                         colorindex = ch - '0';
1632                                         DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1633                                         ++text;
1634                                         ++i;
1635                                         continue;
1636                                 }
1637                                 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1638                                 {
1639                                         // building colorindex...
1640                                         ch = tolower(text[1]);
1641                                         tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1642                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1643                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1644                                         else tempcolorindex = 0;
1645                                         if (tempcolorindex)
1646                                         {
1647                                                 ch = tolower(text[2]);
1648                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1649                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1650                                                 else tempcolorindex = 0;
1651                                                 if (tempcolorindex)
1652                                                 {
1653                                                         ch = tolower(text[3]);
1654                                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1655                                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1656                                                         else tempcolorindex = 0;
1657                                                         if (tempcolorindex)
1658                                                         {
1659                                                                 colorindex = tempcolorindex | 0xf;
1660                                                                 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1661                                                                 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1662                                                                 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1663                                                                 i+=4;
1664                                                                 text+=4;
1665                                                                 continue;
1666                                                         }
1667                                                 }
1668                                         }
1669                                 }
1670                                 else if (ch == STRING_COLOR_TAG)
1671                                 {
1672                                         i++;
1673                                         text++;
1674                                 }
1675                                 i--;
1676                         }
1677                         // get the backup
1678                         ch = nextch;
1679                         // using a value of -1 for the oldstyle map because NULL means uninitialized...
1680                         // this way we don't need to rebind fnt->tex for every old-style character
1681                         // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1682                         if (shadow)
1683                         {
1684                                 x += 1.0/pix_x * r_textshadow.value;
1685                                 y += 1.0/pix_y * r_textshadow.value;
1686                         }
1687                         if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1688                         {
1689                                 if (ch >= 0xE000)
1690                                         ch -= 0xE000;
1691                                 if (ch > 0xFF)
1692                                         goto out;
1693                                 if (fontmap)
1694                                 {
1695                                         if (map != ft2_oldstyle_map)
1696                                         {
1697                                                 if (batchcount)
1698                                                 {
1699                                                         // switching from freetype to non-freetype rendering
1700                                                         R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1701                                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1702                                                         batchcount = 0;
1703                                                         ac = color4f;
1704                                                         at = texcoord2f;
1705                                                         av = vertex3f;
1706                                                 }
1707                                                 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1708                                                 map = ft2_oldstyle_map;
1709                                         }
1710                                 }
1711                                 prevch = 0;
1712                                 //num = (unsigned char) text[i];
1713                                 //thisw = fnt->width_of[num];
1714                                 thisw = fnt->width_of[ch];
1715                                 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1716                                 s = (ch & 15)*0.0625f + (0.5f / tw);
1717                                 t = (ch >> 4)*0.0625f + (0.5f / th);
1718                                 u = 0.0625f * thisw - (1.0f / tw);
1719                                 v = 0.0625f - (1.0f / th);
1720                                 ac[ 0] = DrawQ_Color[0];ac[ 1] = DrawQ_Color[1];ac[ 2] = DrawQ_Color[2];ac[ 3] = DrawQ_Color[3];
1721                                 ac[ 4] = DrawQ_Color[0];ac[ 5] = DrawQ_Color[1];ac[ 6] = DrawQ_Color[2];ac[ 7] = DrawQ_Color[3];
1722                                 ac[ 8] = DrawQ_Color[0];ac[ 9] = DrawQ_Color[1];ac[10] = DrawQ_Color[2];ac[11] = DrawQ_Color[3];
1723                                 ac[12] = DrawQ_Color[0];ac[13] = DrawQ_Color[1];ac[14] = DrawQ_Color[2];ac[15] = DrawQ_Color[3];
1724                                 at[ 0] = s              ; at[ 1] = t    ;
1725                                 at[ 2] = s+u    ; at[ 3] = t    ;
1726                                 at[ 4] = s+u    ; at[ 5] = t+v  ;
1727                                 at[ 6] = s              ; at[ 7] = t+v  ;
1728                                 av[ 0] = x                      ; av[ 1] = y    ; av[ 2] = 10;
1729                                 av[ 3] = x+dw*thisw     ; av[ 4] = y    ; av[ 5] = 10;
1730                                 av[ 6] = x+dw*thisw     ; av[ 7] = y+dh ; av[ 8] = 10;
1731                                 av[ 9] = x                      ; av[10] = y+dh ; av[11] = 10;
1732                                 ac += 16;
1733                                 at += 8;
1734                                 av += 12;
1735                                 batchcount++;
1736                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1737                                 {
1738                                         R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1739                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1740                                         batchcount = 0;
1741                                         ac = color4f;
1742                                         at = texcoord2f;
1743                                         av = vertex3f;
1744                                 }
1745                                 x += width_of[ch] * dw;
1746                         } else {
1747                                 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1748                                 {
1749                                         // new charmap - need to render
1750                                         if (batchcount)
1751                                         {
1752                                                 // we need a different character map, render what we currently have:
1753                                                 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1754                                                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1755                                                 batchcount = 0;
1756                                                 ac = color4f;
1757                                                 at = texcoord2f;
1758                                                 av = vertex3f;
1759                                         }
1760                                         // find the new map
1761                                         map = FontMap_FindForChar(fontmap, ch);
1762                                         if (!map)
1763                                         {
1764                                                 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1765                                                 {
1766                                                         shadow = -1;
1767                                                         break;
1768                                                 }
1769                                                 if (!map)
1770                                                 {
1771                                                         // this shouldn't happen
1772                                                         shadow = -1;
1773                                                         break;
1774                                                 }
1775                                         }
1776                                         R_SetupShader_Generic(map->pic->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1777                                 }
1778
1779                                 mapch = ch - map->start;
1780                                 thisw = map->glyphs[mapch].advance_x;
1781
1782                                 //x += ftbase_x;
1783                                 y += ftbase_y;
1784                                 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1785                                 {
1786                                         x += kx * dw;
1787                                         y += ky * dh;
1788                                 }
1789                                 else
1790                                         kx = ky = 0;
1791                                 ac[ 0] = DrawQ_Color[0]; ac[ 1] = DrawQ_Color[1]; ac[ 2] = DrawQ_Color[2]; ac[ 3] = DrawQ_Color[3];
1792                                 ac[ 4] = DrawQ_Color[0]; ac[ 5] = DrawQ_Color[1]; ac[ 6] = DrawQ_Color[2]; ac[ 7] = DrawQ_Color[3];
1793                                 ac[ 8] = DrawQ_Color[0]; ac[ 9] = DrawQ_Color[1]; ac[10] = DrawQ_Color[2]; ac[11] = DrawQ_Color[3];
1794                                 ac[12] = DrawQ_Color[0]; ac[13] = DrawQ_Color[1]; ac[14] = DrawQ_Color[2]; ac[15] = DrawQ_Color[3];
1795                                 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1796                                 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1797                                 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1798                                 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1799                                 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1800                                 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1801                                 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1802                                 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1803                                 //x -= ftbase_x;
1804                                 y -= ftbase_y;
1805
1806                                 x += thisw * dw;
1807                                 ac += 16;
1808                                 at += 8;
1809                                 av += 12;
1810                                 batchcount++;
1811                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1812                                 {
1813                                         R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1814                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1815                                         batchcount = 0;
1816                                         ac = color4f;
1817                                         at = texcoord2f;
1818                                         av = vertex3f;
1819                                 }
1820
1821                                 //prevmap = map;
1822                                 prevch = ch;
1823                         }
1824 out:
1825                         if (shadow)
1826                         {
1827                                 x -= 1.0/pix_x * r_textshadow.value;
1828                                 y -= 1.0/pix_y * r_textshadow.value;
1829                         }
1830                 }
1831         }
1832         if (batchcount > 0)
1833         {
1834                 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1835                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1836         }
1837
1838         if (outcolor)
1839                 *outcolor = colorindex;
1840         
1841         // note: this relies on the proper text (not shadow) being drawn last
1842         return x;
1843 }
1844
1845 float DrawQ_String(float startx, float starty, const char *text, size_t maxlen, float w, float h, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt)
1846 {
1847         return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1848 }
1849
1850 float DrawQ_TextWidth_UntilWidth_TrackColors(const char *text, size_t *maxlen, float w, float h, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
1851 {
1852         return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1853 }
1854
1855 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1856 {
1857         return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1858 }
1859
1860 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1861 {
1862         return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1863 }
1864
1865 #if 0
1866 // not used
1867 // no ^xrgb management
1868 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1869 {
1870         int color, numchars = 0;
1871         char *outputend2c = output2c + maxoutchars - 2;
1872         if (!outcolor || *outcolor == -1)
1873                 color = STRING_COLOR_DEFAULT;
1874         else
1875                 color = *outcolor;
1876         if (!maxreadchars)
1877                 maxreadchars = 1<<30;
1878         textend = text + maxreadchars;
1879         while (text != textend && *text)
1880         {
1881                 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1882                 {
1883                         if (text[1] == STRING_COLOR_TAG)
1884                                 text++;
1885                         else if (text[1] >= '0' && text[1] <= '9')
1886                         {
1887                                 color = text[1] - '0';
1888                                 text += 2;
1889                                 continue;
1890                         }
1891                 }
1892                 if (output2c >= outputend2c)
1893                         break;
1894                 *output2c++ = *text++;
1895                 *output2c++ = color;
1896                 numchars++;
1897         }
1898         output2c[0] = output2c[1] = 0;
1899         if (outcolor)
1900                 *outcolor = color;
1901         return numchars;
1902 }
1903 #endif
1904
1905 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)
1906 {
1907         float floats[36];
1908
1909         _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1910         if(!r_draw2d.integer && !r_draw2d_force)
1911                 return;
1912
1913 //      R_Mesh_ResetTextureState();
1914         if (pic)
1915         {
1916                 if (width == 0)
1917                         width = pic->width;
1918                 if (height == 0)
1919                         height = pic->height;
1920                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1921         }
1922         else
1923                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1924
1925         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1926         floats[0] = floats[9] = x;
1927         floats[1] = floats[4] = y;
1928         floats[3] = floats[6] = x + width;
1929         floats[7] = floats[10] = y + height;
1930         floats[12] = s1;floats[13] = t1;
1931         floats[14] = s2;floats[15] = t2;
1932         floats[16] = s4;floats[17] = t4;
1933         floats[18] = s3;floats[19] = t3;
1934         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1935         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1936         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1937         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1938
1939         R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1940         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1941 }
1942
1943 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1944 {
1945         _DrawQ_Setup();
1946         CHECKGLERROR
1947         if(!r_draw2d.integer && !r_draw2d_force)
1948                 return;
1949         DrawQ_ProcessDrawFlag(flags, hasalpha);
1950
1951 //      R_Mesh_ResetTextureState();
1952         R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
1953
1954         R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1955         R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1956 }
1957
1958 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1959 {
1960         int num;
1961
1962         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1963         if(!r_draw2d.integer && !r_draw2d_force)
1964                 return;
1965
1966         GL_Color(1,1,1,1);
1967         switch(vid.renderpath)
1968         {
1969         case RENDERPATH_GL11:
1970         case RENDERPATH_GL13:
1971         case RENDERPATH_GL20:
1972 #ifndef USE_GLES2
1973                 CHECKGLERROR
1974                 qglBegin(GL_LINE_LOOP);
1975                 for (num = 0;num < mesh->num_vertices;num++)
1976                 {
1977                         if (mesh->data_color4f)
1978                                 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]);
1979                         qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1980                 }
1981                 qglEnd();
1982                 CHECKGLERROR
1983 #endif
1984                 break;
1985         case RENDERPATH_D3D9:
1986                 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1987                 break;
1988         case RENDERPATH_D3D10:
1989                 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1990                 break;
1991         case RENDERPATH_D3D11:
1992                 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1993                 break;
1994         case RENDERPATH_SOFT:
1995                 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1996                 break;
1997         case RENDERPATH_GLES1:
1998         case RENDERPATH_GLES2:
1999                 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2000                 return;
2001         }
2002 }
2003
2004 //[515]: this is old, delete
2005 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
2006 {
2007         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
2008         if(!r_draw2d.integer && !r_draw2d_force)
2009                 return;
2010
2011         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
2012
2013         switch(vid.renderpath)
2014         {
2015         case RENDERPATH_GL11:
2016         case RENDERPATH_GL13:
2017         case RENDERPATH_GL20:
2018 #ifndef USE_GLES2
2019                 CHECKGLERROR
2020
2021                 //qglLineWidth(width);CHECKGLERROR
2022
2023                 GL_Color(r,g,b,alpha);
2024                 CHECKGLERROR
2025                 qglBegin(GL_LINES);
2026                 qglVertex2f(x1, y1);
2027                 qglVertex2f(x2, y2);
2028                 qglEnd();
2029                 CHECKGLERROR
2030 #endif
2031                 break;
2032         case RENDERPATH_D3D9:
2033                 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2034                 break;
2035         case RENDERPATH_D3D10:
2036                 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2037                 break;
2038         case RENDERPATH_D3D11:
2039                 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2040                 break;
2041         case RENDERPATH_SOFT:
2042                 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2043                 break;
2044         case RENDERPATH_GLES1:
2045         case RENDERPATH_GLES2:
2046                 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2047                 return;
2048         }
2049 }
2050
2051 void DrawQ_Lines (float width, int numlines, const float *vertex3f, const float *color4f, int flags)
2052 {
2053         int i;
2054         qboolean hasalpha = false;
2055         for (i = 0;i < numlines*2;i++)
2056                 if (color4f[i*4+3] < 1.0f)
2057                         hasalpha = true;
2058
2059         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, hasalpha ? 0.5f : 1.0f);
2060
2061         if(!r_draw2d.integer && !r_draw2d_force)
2062                 return;
2063
2064         switch(vid.renderpath)
2065         {
2066         case RENDERPATH_GL11:
2067         case RENDERPATH_GL13:
2068         case RENDERPATH_GL20:
2069                 CHECKGLERROR
2070
2071                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true);
2072
2073                 //qglLineWidth(width);CHECKGLERROR
2074
2075                 CHECKGLERROR
2076                 R_Mesh_PrepareVertices_Generic_Arrays(numlines*2, vertex3f, color4f, NULL);
2077                 qglDrawArrays(GL_LINES, 0, numlines*2);
2078                 CHECKGLERROR
2079                 break;
2080         case RENDERPATH_D3D9:
2081                 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2082                 break;
2083         case RENDERPATH_D3D10:
2084                 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2085                 break;
2086         case RENDERPATH_D3D11:
2087                 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2088                 break;
2089         case RENDERPATH_SOFT:
2090                 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2091                 break;
2092         case RENDERPATH_GLES1:
2093         case RENDERPATH_GLES2:
2094                 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2095                 return;
2096         }
2097 }
2098
2099 void DrawQ_SetClipArea(float x, float y, float width, float height)
2100 {
2101         int ix, iy, iw, ih;
2102         _DrawQ_Setup();
2103
2104         // We have to convert the con coords into real coords
2105         // OGL uses top to bottom
2106         ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
2107         iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
2108         iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
2109         ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
2110         switch(vid.renderpath)
2111         {
2112         case RENDERPATH_GL11:
2113         case RENDERPATH_GL13:
2114         case RENDERPATH_GL20:
2115         case RENDERPATH_GLES1:
2116         case RENDERPATH_GLES2:
2117         case RENDERPATH_SOFT:
2118                 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
2119                 break;
2120         case RENDERPATH_D3D9:
2121                 GL_Scissor(ix, iy, iw, ih);
2122                 break;
2123         case RENDERPATH_D3D10:
2124                 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2125                 break;
2126         case RENDERPATH_D3D11:
2127                 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2128                 break;
2129         }
2130
2131         GL_ScissorTest(true);
2132 }
2133
2134 void DrawQ_ResetClipArea(void)
2135 {
2136         _DrawQ_Setup();
2137         GL_ScissorTest(false);
2138 }
2139
2140 void DrawQ_Finish(void)
2141 {
2142         r_refdef.draw2dstage = 0;
2143 }
2144
2145 void DrawQ_RecalcView(void)
2146 {
2147         if(r_refdef.draw2dstage)
2148                 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2149 }
2150
2151 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2152 void R_DrawGamma(void)
2153 {
2154         float c[4];
2155         switch(vid.renderpath)
2156         {
2157         case RENDERPATH_GL20:
2158         case RENDERPATH_D3D9:
2159         case RENDERPATH_D3D10:
2160         case RENDERPATH_D3D11:
2161         case RENDERPATH_GLES2:
2162                 if (vid_usinghwgamma || v_glslgamma.integer)
2163                         return;
2164                 break;
2165         case RENDERPATH_GL11:
2166         case RENDERPATH_GL13:
2167                 if (vid_usinghwgamma)
2168                         return;
2169                 break;
2170         case RENDERPATH_GLES1:
2171         case RENDERPATH_SOFT:
2172                 return;
2173         }
2174         // all the blends ignore depth
2175 //      R_Mesh_ResetTextureState();
2176         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, true, true);
2177         GL_DepthMask(true);
2178         GL_DepthRange(0, 1);
2179         GL_PolygonOffset(0, 0);
2180         GL_DepthTest(false);
2181
2182         // interpretation of brightness and contrast:
2183         //   color range := brightness .. (brightness + contrast)
2184         // i.e. "c *= contrast; c += brightness"
2185         // plausible values for brightness thus range from -contrast to 1
2186
2187         // apply pre-brightness (subtractive brightness, for where contrast was >= 1)
2188         if (vid.support.ext_blend_subtract)
2189         {
2190                 if (v_color_enable.integer)
2191                 {
2192                         c[0] = -v_color_black_r.value / v_color_white_r.value;
2193                         c[1] = -v_color_black_g.value / v_color_white_g.value;
2194                         c[2] = -v_color_black_b.value / v_color_white_b.value;
2195                 }
2196                 else
2197                         c[0] = c[1] = c[2] = -v_brightness.value / v_contrast.value;
2198                 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2199                 {
2200                         // need SUBTRACTIVE blending to do this!
2201                         GL_BlendEquationSubtract(true);
2202                         GL_BlendFunc(GL_ONE, GL_ONE);
2203                         GL_Color(c[0], c[1], c[2], 1);
2204                         R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2205                         R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2206                         GL_BlendEquationSubtract(false);
2207                 }
2208         }
2209
2210         // apply contrast
2211         if (v_color_enable.integer)
2212         {
2213                 c[0] = v_color_white_r.value;
2214                 c[1] = v_color_white_g.value;
2215                 c[2] = v_color_white_b.value;
2216         }
2217         else
2218                 c[0] = c[1] = c[2] = v_contrast.value;
2219         if (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2220         {
2221                 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2222                 while (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2223                 {
2224                         float cc[4];
2225                         cc[0] = bound(0, c[0] - 1, 1);
2226                         cc[1] = bound(0, c[1] - 1, 1);
2227                         cc[2] = bound(0, c[2] - 1, 1);
2228                         GL_Color(cc[0], cc[1], cc[2], 1);
2229                         R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2230                         R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2231                         c[0] /= 1 + cc[0];
2232                         c[1] /= 1 + cc[1];
2233                         c[2] /= 1 + cc[2];
2234                 }
2235         }
2236         if (c[0] <= 0.997f || c[1] <= 0.997f || c[2] <= 0.997f)
2237         {
2238                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
2239                 GL_Color(c[0], c[1], c[2], 1);
2240                 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2241                 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2242         }
2243
2244         // apply post-brightness (additive brightness, for where contrast was <= 1)
2245         if (v_color_enable.integer)
2246         {
2247                 c[0] = v_color_black_r.value;
2248                 c[1] = v_color_black_g.value;
2249                 c[2] = v_color_black_b.value;
2250         }
2251         else
2252                 c[0] = c[1] = c[2] = v_brightness.value;
2253         if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2254         {
2255                 GL_BlendFunc(GL_ONE, GL_ONE);
2256                 GL_Color(c[0], c[1], c[2], 1);
2257                 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2258                 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2259         }
2260 }
2261