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