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