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