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