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