automatically load/unload cachepic_t textures from disk on demand, this
[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 dp_font_t dp_fonts[MAX_FONTS] = {{0}};
29
30 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)"};
31 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)"};
32 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)"};
33
34 extern cvar_t v_glslgamma;
35
36 //=============================================================================
37 /* Support Routines */
38
39 #define FONT_FILESIZE 13468
40 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
41 static cachepic_t cachepics[MAX_CACHED_PICS];
42 static int numcachepics;
43
44 static rtexturepool_t *drawtexturepool;
45
46 static const unsigned char concharimage[FONT_FILESIZE] =
47 {
48 #include "lhfont.h"
49 };
50
51 static rtexture_t *draw_generateconchars(void)
52 {
53         int i;
54         unsigned char buffer[65536][4], *data = NULL;
55         double random;
56
57         data = LoadTGA_BGRA (concharimage, FONT_FILESIZE);
58 // Gold numbers
59         for (i = 0;i < 8192;i++)
60         {
61                 random = lhrandom (0.0,1.0);
62                 buffer[i][2] = 83 + (unsigned char)(random * 64);
63                 buffer[i][1] = 71 + (unsigned char)(random * 32);
64                 buffer[i][0] = 23 + (unsigned char)(random * 16);
65                 buffer[i][3] = data[i*4+0];
66         }
67 // White chars
68         for (i = 8192;i < 32768;i++)
69         {
70                 random = lhrandom (0.0,1.0);
71                 buffer[i][2] = 95 + (unsigned char)(random * 64);
72                 buffer[i][1] = 95 + (unsigned char)(random * 64);
73                 buffer[i][0] = 95 + (unsigned char)(random * 64);
74                 buffer[i][3] = data[i*4+0];
75         }
76 // Gold numbers
77         for (i = 32768;i < 40960;i++)
78         {
79                 random = lhrandom (0.0,1.0);
80                 buffer[i][2] = 83 + (unsigned char)(random * 64);
81                 buffer[i][1] = 71 + (unsigned char)(random * 32);
82                 buffer[i][0] = 23 + (unsigned char)(random * 16);
83                 buffer[i][3] = data[i*4+0];
84         }
85 // Red chars
86         for (i = 40960;i < 65536;i++)
87         {
88                 random = lhrandom (0.0,1.0);
89                 buffer[i][2] = 96 + (unsigned char)(random * 64);
90                 buffer[i][1] = 43 + (unsigned char)(random * 32);
91                 buffer[i][0] = 27 + (unsigned char)(random * 32);
92                 buffer[i][3] = data[i*4+0];
93         }
94
95 #if 0
96         Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, &buffer[0][0]);
97 #endif
98
99         Mem_Free(data);
100         return R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, &buffer[0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
101 }
102
103 static rtexture_t *draw_generateditherpattern(void)
104 {
105         int x, y;
106         unsigned char pixels[8][8];
107         for (y = 0;y < 8;y++)
108                 for (x = 0;x < 8;x++)
109                         pixels[y][x] = ((x^y) & 4) ? 254 : 0;
110         return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST | TEXF_PRECACHE, palette_bgra_transparent);
111 }
112
113 typedef struct embeddedpic_s
114 {
115         const char *name;
116         int width;
117         int height;
118         const char *pixels;
119 }
120 embeddedpic_t;
121
122 static const embeddedpic_t embeddedpics[] =
123 {
124         {
125         "gfx/prydoncursor001", 16, 16,
126         "477777774......."
127         "77.....6........"
128         "7.....6........."
129         "7....6.........."
130         "7.....6........."
131         "7..6...6........"
132         "7.6.6...6......."
133         "76...6...6......"
134         "4.....6.6......."
135         ".......6........"
136         "................"
137         "................"
138         "................"
139         "................"
140         "................"
141         "................"
142         },
143         {
144         "ui/mousepointer", 16, 16,
145         "477777774......."
146         "77.....6........"
147         "7.....6........."
148         "7....6.........."
149         "7.....6........."
150         "7..6...6........"
151         "7.6.6...6......."
152         "76...6...6......"
153         "4.....6.6......."
154         ".......6........"
155         "................"
156         "................"
157         "................"
158         "................"
159         "................"
160         "................"
161         },
162         {
163         "gfx/crosshair1", 16, 16,
164         "................"
165         "................"
166         "................"
167         "...33......33..."
168         "...355....553..."
169         "....577..775...."
170         ".....77..77....."
171         "................"
172         "................"
173         ".....77..77....."
174         "....577..775...."
175         "...355....553..."
176         "...33......33..."
177         "................"
178         "................"
179         "................"
180         },
181         {
182         "gfx/crosshair2", 16, 16,
183         "................"
184         "................"
185         "................"
186         "...3........3..."
187         "....5......5...."
188         ".....7....7....."
189         "......7..7......"
190         "................"
191         "................"
192         "......7..7......"
193         ".....7....7....."
194         "....5......5...."
195         "...3........3..."
196         "................"
197         "................"
198         "................"
199         },
200         {
201         "gfx/crosshair3", 16, 16,
202         "................"
203         ".......77......."
204         ".......77......."
205         "................"
206         "................"
207         ".......44......."
208         ".......44......."
209         ".77..44..44..77."
210         ".77..44..44..77."
211         ".......44......."
212         ".......44......."
213         "................"
214         "................"
215         ".......77......."
216         ".......77......."
217         "................"
218         },
219         {
220         "gfx/crosshair4", 16, 16,
221         "................"
222         "................"
223         "................"
224         "................"
225         "................"
226         "................"
227         "................"
228         "................"
229         "........7777777."
230         "........752....."
231         "........72......"
232         "........7......."
233         "........7......."
234         "........7......."
235         "........7......."
236         "................"
237         },
238         {
239         "gfx/crosshair5", 8, 8,
240         "........"
241         "........"
242         "....7..."
243         "........"
244         "..7.7.7."
245         "........"
246         "....7..."
247         "........"
248         },
249         {
250         "gfx/crosshair6", 2, 2,
251         "77"
252         "77"
253         },
254         {
255         "gfx/crosshair7", 16, 16,
256         "................"
257         ".3............3."
258         "..5...2332...5.."
259         "...7.3....3.7..."
260         "....7......7...."
261         "...3.7....7.3..."
262         "..2...7..7...2.."
263         "..3..........3.."
264         "..3..........3.."
265         "..2...7..7...2.."
266         "...3.7....7.3..."
267         "....7......7...."
268         "...7.3....3.7..."
269         "..5...2332...5.."
270         ".3............3."
271         "................"
272         },
273         {NULL, 0, 0, NULL}
274 };
275
276 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
277 {
278         const embeddedpic_t *p;
279         for (p = embeddedpics;p->name;p++)
280                 if (!strcmp(name, p->name))
281                         return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA | TEXF_PRECACHE, palette_bgra_embeddedpic);
282         if (!strcmp(name, "gfx/conchars"))
283                 return draw_generateconchars();
284         if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
285                 return draw_generateditherpattern();
286         if (!quiet)
287                 Con_Printf("Draw_CachePic: failed to load %s\n", name);
288         return r_texture_notexture;
289 }
290
291
292 /*
293 ================
294 Draw_CachePic
295 ================
296 */
297 // FIXME: move this to client somehow
298 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
299 {
300         int crc, hashkey;
301         unsigned char *pixels;
302         cachepic_t *pic;
303         fs_offset_t lmpsize;
304         unsigned char *lmpdata;
305         char lmpname[MAX_QPATH];
306
307         // check whether the picture has already been cached
308         crc = CRC_Block((unsigned char *)path, strlen(path));
309         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
310         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
311                 if (!strcmp (path, pic->name))
312                         return pic;
313
314         if (numcachepics == MAX_CACHED_PICS)
315         {
316                 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
317                 // FIXME: support NULL in callers?
318                 return cachepics; // return the first one
319         }
320         pic = cachepics + (numcachepics++);
321         strlcpy (pic->name, path, sizeof(pic->name));
322         // link into list
323         pic->chain = cachepichash[hashkey];
324         cachepichash[hashkey] = pic;
325
326         // check whether it is an dynamic texture (if so, we can directly use its texture handler)
327         pic->tex = CL_GetDynTexture( path );
328         // if so, set the width/height, too
329         if( pic->tex ) {
330                 pic->width = R_TextureWidth(pic->tex);
331                 pic->height = R_TextureHeight(pic->tex);
332                 // we're done now (early-out)
333                 return pic;
334         }
335
336         pic->texflags = TEXF_ALPHA | TEXF_PRECACHE;
337         if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
338                 pic->texflags |= TEXF_CLAMP;
339         if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer)
340                 pic->texflags |= TEXF_COMPRESS;
341
342         pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
343
344         // load a high quality image from disk if possible
345         pixels = loadimagepixelsbgra(path, false, true);
346         if (pixels == NULL && !strncmp(path, "gfx/", 4))
347                 pixels = loadimagepixelsbgra(path+4, false, true);
348         if (pixels)
349         {
350                 pic->width = image_width;
351                 pic->height = image_height;
352                 if (!pic->autoload)
353                         pic->tex = R_LoadTexture2D(drawtexturepool, path, image_width, image_height, pixels, TEXTYPE_BGRA, pic->texflags, NULL);
354         }
355         else
356         {
357                 pic->autoload = false;
358                 // never compress the fallback images
359                 pic->texflags &= ~TEXF_COMPRESS;
360         }
361
362         // now read the low quality version (wad or lmp file), and take the pic
363         // size from that even if we don't upload the texture, this way the pics
364         // show up the right size in the menu even if they were replaced with
365         // higher or lower resolution versions
366         dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
367         if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
368         {
369                 if (developer_loading.integer)
370                         Con_Printf("loading lump \"%s\"\n", path);
371
372                 if (lmpsize >= 9)
373                 {
374                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
375                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
376                         // if no high quality replacement image was found, upload the original low quality texture
377                         if (!pixels)
378                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, palette_bgra_transparent);
379                 }
380                 Mem_Free(lmpdata);
381         }
382         else if ((lmpdata = W_GetLumpName (path + 4)))
383         {
384                 if (developer_loading.integer)
385                         Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
386
387                 if (!strcmp(path, "gfx/conchars"))
388                 {
389                         // conchars is a raw image and with color 0 as transparent instead of 255
390                         pic->width = 128;
391                         pic->height = 128;
392                         // if no high quality replacement image was found, upload the original low quality texture
393                         if (!pixels)
394                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, pic->texflags, palette_bgra_font);
395                 }
396                 else
397                 {
398                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
399                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
400                         // if no high quality replacement image was found, upload the original low quality texture
401                         if (!pixels)
402                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, palette_bgra_transparent);
403                 }
404         }
405
406         if (pixels)
407         {
408                 Mem_Free(pixels);
409                 pixels = NULL;
410         }
411         else if (pic->tex == NULL)
412         {
413                 // if it's not found on disk, generate an image
414                 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
415                 pic->width = R_TextureWidth(pic->tex);
416                 pic->height = R_TextureHeight(pic->tex);
417         }
418
419         return pic;
420 }
421
422 cachepic_t *Draw_CachePic (const char *path)
423 {
424         return Draw_CachePic_Flags (path, 0);
425 }
426
427 int draw_frame = 1;
428
429 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
430 {
431         if (pic->autoload && !pic->tex)
432         {
433                 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true);
434                 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
435                         pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true);
436                 if (pic->tex == NULL)
437                         pic->tex = draw_generatepic(pic->name, true);
438         }
439         pic->lastusedframe = draw_frame;
440         return pic->tex;
441 }
442
443 void Draw_Frame(void)
444 {
445         int i;
446         cachepic_t *pic;
447         static double nextpurgetime;
448         int purgeframe;
449         if (nextpurgetime > realtime)
450                 return;
451         nextpurgetime = realtime + 0.05;
452         purgeframe = draw_frame - 1;
453         for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
454         {
455                 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
456                 {
457                         R_FreeTexture(pic->tex);
458                         pic->tex = NULL;
459                 }
460         }
461         draw_frame++;
462 }
463
464 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
465 {
466         int crc, hashkey;
467         cachepic_t *pic;
468
469         crc = CRC_Block((unsigned char *)picname, strlen(picname));
470         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
471         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
472                 if (!strcmp (picname, pic->name))
473                         break;
474
475         if (pic)
476         {
477                 if (pic->tex && pic->width == width && pic->height == height)
478                 {
479                         R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
480                         return pic;
481                 }
482         }
483         else
484         {
485                 if (pic == NULL)
486                 {
487                         if (numcachepics == MAX_CACHED_PICS)
488                         {
489                                 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
490                                 // FIXME: support NULL in callers?
491                                 return cachepics; // return the first one
492                         }
493                         pic = cachepics + (numcachepics++);
494                         strlcpy (pic->name, picname, sizeof(pic->name));
495                         // link into list
496                         pic->chain = cachepichash[hashkey];
497                         cachepichash[hashkey] = pic;
498                 }
499         }
500
501         pic->width = width;
502         pic->height = height;
503         if (pic->tex)
504                 R_FreeTexture(pic->tex);
505         pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, alpha ? TEXF_ALPHA : 0, NULL);
506         return pic;
507 }
508
509 void Draw_FreePic(const char *picname)
510 {
511         int crc;
512         int hashkey;
513         cachepic_t *pic;
514         // this doesn't really free the pic, but does free it's texture
515         crc = CRC_Block((unsigned char *)picname, strlen(picname));
516         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
517         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
518         {
519                 if (!strcmp (picname, pic->name) && pic->tex)
520                 {
521                         R_FreeTexture(pic->tex);
522                         pic->width = 0;
523                         pic->height = 0;
524                         return;
525                 }
526         }
527 }
528
529 extern int con_linewidth; // to force rewrapping
530 static void LoadFont(qboolean override, const char *name, dp_font_t *fnt)
531 {
532         int i;
533         float maxwidth, scale;
534         char widthfile[MAX_QPATH];
535         char *widthbuf;
536         fs_offset_t widthbufsize;
537
538         if(override || !fnt->texpath[0])
539                 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
540
541         if(drawtexturepool == NULL)
542                 return; // before gl_draw_start, so will be loaded later
543
544         fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
545         if(fnt->tex == r_texture_notexture)
546         {
547                 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
548                 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
549         }
550         else
551                 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
552
553         // unspecified width == 1 (base width)
554         for(i = 1; i < 256; ++i)
555                 fnt->width_of[i] = 1;
556         scale = 1;
557
558         // FIXME load "name.width", if it fails, fill all with 1
559         if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
560         {
561                 float extraspacing = 0;
562                 const char *p = widthbuf;
563                 int ch = 0;
564
565                 while(ch < 256)
566                 {
567                         if(!COM_ParseToken_Simple(&p, false, false))
568                                 return;
569
570                         switch(*com_token)
571                         {
572                                 case '0':
573                                 case '1':
574                                 case '2':
575                                 case '3':
576                                 case '4':
577                                 case '5':
578                                 case '6':
579                                 case '7':
580                                 case '8':
581                                 case '9':
582                                 case '+':
583                                 case '-':
584                                 case '.':
585                                         fnt->width_of[ch++] = atof(com_token) + extraspacing;
586                                         break;
587                                 default:
588                                         if(!strcmp(com_token, "extraspacing"))
589                                         {
590                                                 if(!COM_ParseToken_Simple(&p, false, false))
591                                                         return;
592                                                 extraspacing = atof(com_token);
593                                         }
594                                         else if(!strcmp(com_token, "scale"))
595                                         {
596                                                 if(!COM_ParseToken_Simple(&p, false, false))
597                                                         return;
598                                                 scale = atof(com_token);
599                                         }
600                                         else
601                                         {
602                                                 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
603                                                 if(!COM_ParseToken_Simple(&p, false, false))
604                                                         return;
605                                         }
606                                         break;
607                         }
608                 }
609
610                 Mem_Free(widthbuf);
611         }
612
613         maxwidth = fnt->width_of[1];
614         for(i = 2; i < 256; ++i)
615                 maxwidth = max(maxwidth, fnt->width_of[i]);
616         fnt->maxwidth = maxwidth;
617
618         // fix up maxwidth for overlap
619         fnt->maxwidth *= scale;
620         fnt->scale = scale;
621
622         if(fnt == FONT_CONSOLE)
623                 con_linewidth = -1; // rewrap console in next frame
624 }
625
626 static dp_font_t *FindFont(const char *title)
627 {
628         int i;
629         for(i = 0; i < MAX_FONTS; ++i)
630                 if(!strcmp(dp_fonts[i].title, title))
631                         return &dp_fonts[i];
632         return NULL;
633 }
634
635 static void LoadFont_f(void)
636 {
637         dp_font_t *f;
638         int i;
639         if(Cmd_Argc() < 2)
640         {
641                 Con_Printf("Available font commands:\n");
642                 for(i = 0; i < MAX_FONTS; ++i)
643                         Con_Printf("  loadfont %s gfx/tgafile\n", dp_fonts[i].title);
644                 return;
645         }
646         f = FindFont(Cmd_Argv(1));
647         if(f == NULL)
648         {
649                 Con_Printf("font function not found\n");
650                 return;
651         }
652         LoadFont(true, (Cmd_Argc() < 3) ? "gfx/conchars" : Cmd_Argv(2), f);
653 }
654
655 /*
656 ===============
657 Draw_Init
658 ===============
659 */
660 static void gl_draw_start(void)
661 {
662         int i;
663         drawtexturepool = R_AllocTexturePool();
664
665         numcachepics = 0;
666         memset(cachepichash, 0, sizeof(cachepichash));
667
668         for(i = 0; i < MAX_FONTS; ++i)
669                 LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i]);
670
671         // draw the loading screen so people have something to see in the newly opened window
672         SCR_UpdateLoadingScreen(true);
673 }
674
675 static void gl_draw_shutdown(void)
676 {
677         R_FreeTexturePool(&drawtexturepool);
678
679         numcachepics = 0;
680         memset(cachepichash, 0, sizeof(cachepichash));
681 }
682
683 static void gl_draw_newmap(void)
684 {
685 }
686
687 void GL_Draw_Init (void)
688 {
689         int i, j;
690         Cvar_RegisterVariable(&r_textshadow);
691         Cvar_RegisterVariable(&r_textbrightness);
692         Cvar_RegisterVariable(&r_textcontrast);
693         Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
694         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
695
696         strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
697                 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
698         strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
699         strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
700         strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
701         strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
702         strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
703         strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
704         strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
705         for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
706                 if(!FONT_USER[i].title[0])
707                         dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++);
708 }
709
710 void _DrawQ_Setup(void)
711 {
712         r_viewport_t viewport;
713         if (r_refdef.draw2dstage)
714                 return;
715         r_refdef.draw2dstage = true;
716         CHECKGLERROR
717         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);
718         R_SetViewport(&viewport);
719         GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
720         qglDepthFunc(GL_LEQUAL);CHECKGLERROR
721         qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
722         GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
723         R_Mesh_Matrix(&identitymatrix);
724
725         GL_DepthMask(true);
726         GL_DepthRange(0, 1);
727         GL_PolygonOffset(0, 0);
728         GL_DepthTest(false);
729         GL_Color(1,1,1,1);
730         GL_AlphaTest(false);
731         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
732
733         R_SetupGenericShader(true);
734 }
735
736 static void _DrawQ_ProcessDrawFlag(int flags)
737 {
738         _DrawQ_Setup();
739         CHECKGLERROR
740         if(flags == DRAWFLAG_ADDITIVE)
741                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
742         else if(flags == DRAWFLAG_MODULATE)
743                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
744         else if(flags == DRAWFLAG_2XMODULATE)
745                 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
746         else if(flags == DRAWFLAG_SCREEN)
747                 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
748         else
749                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
750 }
751
752 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
753 {
754         float floats[20];
755
756         _DrawQ_ProcessDrawFlag(flags);
757         GL_Color(red, green, blue, alpha);
758
759         R_Mesh_VertexPointer(floats, 0, 0);
760         R_Mesh_ColorPointer(NULL, 0, 0);
761         R_Mesh_ResetTextureState();
762         R_SetupGenericShader(pic != NULL);
763         if (pic)
764         {
765                 if (width == 0)
766                         width = pic->width;
767                 if (height == 0)
768                         height = pic->height;
769                 R_Mesh_TexBind(0, R_GetTexture(Draw_GetPicTexture(pic)));
770                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
771
772 #if 1
773                 floats[12] = 0.0f;floats[13] = 0.0f;
774                 floats[14] = 1.0f;floats[15] = 0.0f;
775                 floats[16] = 1.0f;floats[17] = 1.0f;
776                 floats[18] = 0.0f;floats[19] = 1.0f;
777 #else
778       // AK07: lets be texel correct on the corners
779       {
780          float horz_offset = 0.5f / pic->width;
781          float vert_offset = 0.5f / pic->height;
782
783                    floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
784                    floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
785                    floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
786                    floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
787       }
788 #endif
789         }
790
791         floats[2] = floats[5] = floats[8] = floats[11] = 0;
792         floats[0] = floats[9] = x;
793         floats[1] = floats[4] = y;
794         floats[3] = floats[6] = x + width;
795         floats[7] = floats[10] = y + height;
796
797         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
798 }
799
800 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)
801 {
802         float floats[20];
803         float af = DEG2RAD(-angle); // forward
804         float ar = DEG2RAD(-angle + 90); // right
805         float sinaf = sin(af);
806         float cosaf = cos(af);
807         float sinar = sin(ar);
808         float cosar = cos(ar);
809
810         _DrawQ_ProcessDrawFlag(flags);
811         GL_Color(red, green, blue, alpha);
812
813         R_Mesh_VertexPointer(floats, 0, 0);
814         R_Mesh_ColorPointer(NULL, 0, 0);
815         R_Mesh_ResetTextureState();
816         R_SetupGenericShader(pic != NULL);
817         if (pic)
818         {
819                 if (width == 0)
820                         width = pic->width;
821                 if (height == 0)
822                         height = pic->height;
823                 R_Mesh_TexBind(0, R_GetTexture(Draw_GetPicTexture(pic)));
824                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
825
826                 floats[12] = 0.0f;floats[13] = 0.0f;
827                 floats[14] = 1.0f;floats[15] = 0.0f;
828                 floats[16] = 1.0f;floats[17] = 1.0f;
829                 floats[18] = 0.0f;floats[19] = 1.0f;
830         }
831
832         floats[2] = floats[5] = floats[8] = floats[11] = 0;
833
834 // top left
835         floats[0] = x - cosaf*org_x - cosar*org_y;
836         floats[1] = y - sinaf*org_x - sinar*org_y;
837
838 // top right
839         floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
840         floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
841
842 // bottom right
843         floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
844         floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
845
846 // bottom left
847         floats[9]  = x - cosaf*org_x + cosar*(height-org_y);
848         floats[10] = y - sinaf*org_x + sinar*(height-org_y);
849
850         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
851 }
852
853 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
854 {
855         float floats[12];
856
857         _DrawQ_ProcessDrawFlag(flags);
858         GL_Color(red, green, blue, alpha);
859
860         R_Mesh_VertexPointer(floats, 0, 0);
861         R_Mesh_ColorPointer(NULL, 0, 0);
862         R_Mesh_ResetTextureState();
863         R_SetupGenericShader(false);
864
865         floats[2] = floats[5] = floats[8] = floats[11] = 0;
866         floats[0] = floats[9] = x;
867         floats[1] = floats[4] = y;
868         floats[3] = floats[6] = x + width;
869         floats[7] = floats[10] = y + height;
870
871         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
872 }
873
874 /// color tag printing
875 static const vec4_t string_colors[] =
876 {
877         // Quake3 colors
878         // LordHavoc: why on earth is cyan before magenta in Quake3?
879         // LordHavoc: note: Doom3 uses white for [0] and [7]
880         {0.0, 0.0, 0.0, 1.0}, // black
881         {1.0, 0.0, 0.0, 1.0}, // red
882         {0.0, 1.0, 0.0, 1.0}, // green
883         {1.0, 1.0, 0.0, 1.0}, // yellow
884         {0.0, 0.0, 1.0, 1.0}, // blue
885         {0.0, 1.0, 1.0, 1.0}, // cyan
886         {1.0, 0.0, 1.0, 1.0}, // magenta
887         {1.0, 1.0, 1.0, 1.0}, // white
888         // [515]'s BX_COLOREDTEXT extension
889         {1.0, 1.0, 1.0, 0.5}, // half transparent
890         {0.5, 0.5, 0.5, 1.0}  // half brightness
891         // Black's color table
892         //{1.0, 1.0, 1.0, 1.0},
893         //{1.0, 0.0, 0.0, 1.0},
894         //{0.0, 1.0, 0.0, 1.0},
895         //{0.0, 0.0, 1.0, 1.0},
896         //{1.0, 1.0, 0.0, 1.0},
897         //{0.0, 1.0, 1.0, 1.0},
898         //{1.0, 0.0, 1.0, 1.0},
899         //{0.1, 0.1, 0.1, 1.0}
900 };
901
902 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
903
904 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
905 {
906         float C = r_textcontrast.value;
907         float B = r_textbrightness.value;
908         if (colorindex & 0x10000) // that bit means RGB color
909         {
910                 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
911                 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
912                 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
913                 color[3] = (colorindex & 0xf) / 15.0;
914         }
915         else
916                 Vector4Copy(string_colors[colorindex], color);
917         Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
918         if (shadow)
919         {
920                 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
921                 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
922         }
923 }
924
925 float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
926 {
927         int num, colorindex = STRING_COLOR_DEFAULT;
928         size_t i;
929         float x = 0;
930         char ch;
931         int tempcolorindex;
932
933         if (*maxlen < 1)
934                 *maxlen = 1<<30;
935
936         if (!outcolor || *outcolor == -1)
937                 colorindex = STRING_COLOR_DEFAULT;
938         else
939                 colorindex = *outcolor;
940
941         maxwidth /= fnt->scale;
942
943         for (i = 0;i < *maxlen && text[i];i++)
944         {
945                 if (text[i] == ' ')
946                 {
947                         if(x + fnt->width_of[(int) ' '] > maxwidth)
948                                 break; // oops, can't draw this
949                         x += fnt->width_of[(int) ' '];
950                         continue;
951                 }
952                 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen)
953                 {
954                         ch = text[++i];
955             if (ch <= '9' && ch >= '0') // ^[0-9] found
956                         {
957                                 colorindex = ch - '0';
958                 continue;
959                         }
960                         else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
961                         {
962                                 // building colorindex...
963                                 ch = tolower(text[i+1]);
964                                 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
965                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
966                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
967                                 else tempcolorindex = 0;
968                                 if (tempcolorindex)
969                                 {
970                                         ch = tolower(text[i+2]);
971                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
972                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
973                                         else tempcolorindex = 0;
974                                         if (tempcolorindex)
975                                         {
976                                                 ch = tolower(text[i+3]);
977                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
978                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
979                                                 else tempcolorindex = 0;
980                                                 if (tempcolorindex)
981                                                 {
982                                                         colorindex = tempcolorindex | 0xf;
983                                                         // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
984                                                         i+=3;
985                                                         continue;
986                                                 }
987                                         }
988                                 }
989                         }
990                         else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
991                                 i++;
992                         i--;
993                 }
994                 num = (unsigned char) text[i];
995                 if(x + fnt->width_of[num] > maxwidth)
996                         break; // oops, can't draw this
997                 x += fnt->width_of[num];
998         }
999
1000         *maxlen = i;
1001
1002         if (outcolor)
1003                 *outcolor = colorindex;
1004
1005         return x * fnt->scale;
1006 }
1007
1008 float DrawQ_String_Font(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)
1009 {
1010         int num, shadow, colorindex = STRING_COLOR_DEFAULT;
1011         size_t i;
1012         float x = startx, y, s, t, u, v, thisw;
1013         float *av, *at, *ac;
1014         float color[4];
1015         int batchcount;
1016         float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1017         float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1018         float color4f[QUADELEMENTS_MAXQUADS*4*4];
1019         int ch;
1020         int tempcolorindex;
1021
1022         int tw, th;
1023         tw = R_TextureWidth(fnt->tex);
1024         th = R_TextureHeight(fnt->tex);
1025
1026         starty -= (fnt->scale - 1) * h * 0.5; // center
1027         w *= fnt->scale;
1028         h *= fnt->scale;
1029
1030         if (maxlen < 1)
1031                 maxlen = 1<<30;
1032
1033         _DrawQ_ProcessDrawFlag(flags);
1034
1035         R_Mesh_ColorPointer(color4f, 0, 0);
1036         R_Mesh_ResetTextureState();
1037         R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
1038         R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
1039         R_Mesh_VertexPointer(vertex3f, 0, 0);
1040         R_SetupGenericShader(true);
1041
1042         ac = color4f;
1043         at = texcoord2f;
1044         av = vertex3f;
1045         batchcount = 0;
1046
1047         for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1048         {
1049                 if (!outcolor || *outcolor == -1)
1050                         colorindex = STRING_COLOR_DEFAULT;
1051                 else
1052                         colorindex = *outcolor;
1053
1054                 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1055
1056                 x = startx;
1057                 y = starty;
1058                 if (shadow)
1059                 {
1060                         x += r_textshadow.value;
1061                         y += r_textshadow.value;
1062                 }
1063                 for (i = 0;i < maxlen && text[i];i++)
1064                 {
1065                         if (text[i] == ' ')
1066                         {
1067                                 x += fnt->width_of[(int) ' '] * w;
1068                                 continue;
1069                         }
1070                         if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen)
1071                         {
1072                                 ch = text[++i];
1073                                 if (ch <= '9' && ch >= '0') // ^[0-9] found
1074                                 {
1075                                         colorindex = ch - '0';
1076                                         DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1077                                         continue;
1078                                 }
1079                                 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1080                                 {
1081                                         // building colorindex...
1082                                         ch = tolower(text[i+1]);
1083                                         tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1084                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1085                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1086                                         else tempcolorindex = 0;
1087                                         if (tempcolorindex)
1088                                         {
1089                                                 ch = tolower(text[i+2]);
1090                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1091                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1092                                                 else tempcolorindex = 0;
1093                                                 if (tempcolorindex)
1094                                                 {
1095                                                         ch = tolower(text[i+3]);
1096                                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1097                                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1098                                                         else tempcolorindex = 0;
1099                                                         if (tempcolorindex)
1100                                                         {
1101                                                                 colorindex = tempcolorindex | 0xf;
1102                                                                 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1103                                                                 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1104                                                                 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1105                                                                 i+=3;
1106                                                                 continue;
1107                                                         }
1108                                                 }
1109                                         }
1110                                 }
1111                                 else if (ch == STRING_COLOR_TAG)
1112                                         i++;
1113                                 i--;
1114                         }
1115                         num = (unsigned char) text[i];
1116                         thisw = fnt->width_of[num];
1117                         // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1118                         s = (num & 15)*0.0625f + (0.5f / tw);
1119                         t = (num >> 4)*0.0625f + (0.5f / th);
1120                         u = 0.0625f * thisw - (1.0f / tw);
1121                         v = 0.0625f - (1.0f / th);
1122                         ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1123                         ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1124                         ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1125                         ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1126                         at[ 0] = s              ; at[ 1] = t    ;
1127                         at[ 2] = s+u    ; at[ 3] = t    ;
1128                         at[ 4] = s+u    ; at[ 5] = t+v  ;
1129                         at[ 6] = s              ; at[ 7] = t+v  ;
1130                         av[ 0] = x                      ; av[ 1] = y    ; av[ 2] = 10;
1131                         av[ 3] = x+w*thisw      ; av[ 4] = y    ; av[ 5] = 10;
1132                         av[ 6] = x+w*thisw      ; av[ 7] = y+h  ; av[ 8] = 10;
1133                         av[ 9] = x                      ; av[10] = y+h  ; av[11] = 10;
1134                         ac += 16;
1135                         at += 8;
1136                         av += 12;
1137                         batchcount++;
1138                         if (batchcount >= QUADELEMENTS_MAXQUADS)
1139                         {
1140                                 GL_LockArrays(0, batchcount * 4);
1141                                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1142                                 GL_LockArrays(0, 0);
1143                                 batchcount = 0;
1144                                 ac = color4f;
1145                                 at = texcoord2f;
1146                                 av = vertex3f;
1147                         }
1148                         x += thisw * w;
1149                 }
1150         }
1151         if (batchcount > 0)
1152         {
1153                 GL_LockArrays(0, batchcount * 4);
1154                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1155                 GL_LockArrays(0, 0);
1156         }
1157
1158         if (outcolor)
1159                 *outcolor = colorindex;
1160
1161         // note: this relies on the proper text (not shadow) being drawn last
1162         return x;
1163 }
1164
1165 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)
1166 {
1167         return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]);
1168 }
1169
1170 float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt)
1171 {
1172         return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, ignorecolorcodes, fnt, 1000000000);
1173 }
1174
1175 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1176 {
1177         return DrawQ_TextWidth_Font_UntilWidth_TrackColors(text, maxlen, NULL, ignorecolorcodes, fnt, maxWidth);
1178 }
1179
1180 #if 0
1181 // not used
1182 // no ^xrgb management
1183 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1184 {
1185         int color, numchars = 0;
1186         char *outputend2c = output2c + maxoutchars - 2;
1187         if (!outcolor || *outcolor == -1)
1188                 color = STRING_COLOR_DEFAULT;
1189         else
1190                 color = *outcolor;
1191         if (!maxreadchars)
1192                 maxreadchars = 1<<30;
1193         textend = text + maxreadchars;
1194         while (text != textend && *text)
1195         {
1196                 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1197                 {
1198                         if (text[1] == STRING_COLOR_TAG)
1199                                 text++;
1200                         else if (text[1] >= '0' && text[1] <= '9')
1201                         {
1202                                 color = text[1] - '0';
1203                                 text += 2;
1204                                 continue;
1205                         }
1206                 }
1207                 if (output2c >= outputend2c)
1208                         break;
1209                 *output2c++ = *text++;
1210                 *output2c++ = color;
1211                 numchars++;
1212         }
1213         output2c[0] = output2c[1] = 0;
1214         if (outcolor)
1215                 *outcolor = color;
1216         return numchars;
1217 }
1218 #endif
1219
1220 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)
1221 {
1222         float floats[36];
1223
1224         _DrawQ_ProcessDrawFlag(flags);
1225
1226         R_Mesh_VertexPointer(floats, 0, 0);
1227         R_Mesh_ColorPointer(floats + 20, 0, 0);
1228         R_Mesh_ResetTextureState();
1229         R_SetupGenericShader(pic != NULL);
1230         if (pic)
1231         {
1232                 if (width == 0)
1233                         width = pic->width;
1234                 if (height == 0)
1235                         height = pic->height;
1236                 R_Mesh_TexBind(0, R_GetTexture(Draw_GetPicTexture(pic)));
1237                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1238                 floats[12] = s1;floats[13] = t1;
1239                 floats[14] = s2;floats[15] = t2;
1240                 floats[16] = s4;floats[17] = t4;
1241                 floats[18] = s3;floats[19] = t3;
1242         }
1243
1244         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1245         floats[0] = floats[9] = x;
1246         floats[1] = floats[4] = y;
1247         floats[3] = floats[6] = x + width;
1248         floats[7] = floats[10] = y + height;
1249         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1250         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1251         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1252         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1253
1254         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1255 }
1256
1257 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1258 {
1259         _DrawQ_ProcessDrawFlag(flags);
1260
1261         R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1262         R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1263         R_Mesh_ResetTextureState();
1264         R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
1265         R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1266         R_SetupGenericShader(mesh->texture != NULL);
1267
1268         GL_LockArrays(0, mesh->num_vertices);
1269         R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, mesh->data_element3s, 0, 0);
1270         GL_LockArrays(0, 0);
1271 }
1272
1273 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1274 {
1275         int num;
1276
1277         _DrawQ_ProcessDrawFlag(flags);
1278
1279         GL_Color(1,1,1,1);
1280         CHECKGLERROR
1281         qglBegin(GL_LINE_LOOP);
1282         for (num = 0;num < mesh->num_vertices;num++)
1283         {
1284                 if (mesh->data_color4f)
1285                         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]);
1286                 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1287         }
1288         qglEnd();
1289         CHECKGLERROR
1290 }
1291
1292 //[515]: this is old, delete
1293 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1294 {
1295         _DrawQ_ProcessDrawFlag(flags);
1296
1297         R_SetupGenericShader(false);
1298
1299         CHECKGLERROR
1300         //qglLineWidth(width);CHECKGLERROR
1301
1302         GL_Color(r,g,b,alpha);
1303         CHECKGLERROR
1304         qglBegin(GL_LINES);
1305         qglVertex2f(x1, y1);
1306         qglVertex2f(x2, y2);
1307         qglEnd();
1308         CHECKGLERROR
1309 }
1310
1311 void DrawQ_SetClipArea(float x, float y, float width, float height)
1312 {
1313         int ix, iy, iw, ih;
1314         _DrawQ_Setup();
1315
1316         // We have to convert the con coords into real coords
1317         // OGL uses top to bottom
1318         ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1319         iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1320         iw = (int)(width * ((float)vid.width / vid_conwidth.integer));
1321         ih = (int)(height * ((float)vid.height / vid_conheight.integer));
1322         GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1323
1324         GL_ScissorTest(true);
1325 }
1326
1327 void DrawQ_ResetClipArea(void)
1328 {
1329         _DrawQ_Setup();
1330         GL_ScissorTest(false);
1331 }
1332
1333 void DrawQ_Finish(void)
1334 {
1335         r_refdef.draw2dstage = false;
1336 }
1337
1338 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1339 void R_DrawGamma(void)
1340 {
1341         float c[4];
1342         if (!vid_usinghwgamma && !(r_glsl.integer && v_glslgamma.integer))
1343         {
1344                 // all the blends ignore depth
1345                 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1346                 R_Mesh_ColorPointer(NULL, 0, 0);
1347                 R_Mesh_ResetTextureState();
1348                 R_SetupGenericShader(false);
1349                 GL_DepthMask(true);
1350                 GL_DepthRange(0, 1);
1351                 GL_PolygonOffset(0, 0);
1352                 GL_DepthTest(false);
1353                 if (v_color_enable.integer)
1354                 {
1355                         c[0] = v_color_white_r.value;
1356                         c[1] = v_color_white_g.value;
1357                         c[2] = v_color_white_b.value;
1358                 }
1359                 else
1360                         c[0] = c[1] = c[2] = v_contrast.value;
1361                 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1362                 {
1363                         GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1364                         while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1365                         {
1366                                 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1367                                 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
1368                                 VectorScale(c, 0.5, c);
1369                         }
1370                 }
1371                 if (v_color_enable.integer)
1372                 {
1373                         c[0] = v_color_black_r.value;
1374                         c[1] = v_color_black_g.value;
1375                         c[2] = v_color_black_b.value;
1376                 }
1377                 else
1378                         c[0] = c[1] = c[2] = v_brightness.value;
1379                 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1380                 {
1381                         GL_BlendFunc(GL_ONE, GL_ONE);
1382                         GL_Color(c[0], c[1], c[2], 1);
1383                         R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
1384                 }
1385         }
1386 }
1387