]> icculus.org git repositories - divverent/darkplaces.git/blob - gl_draw.c
also put svn revision (svnversion(1)) and build type into the version string
[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 #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 (strcmp(path, "gfx/colorcontrol/ditherpattern"))
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;
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
628         // FIXME load "name.width", if it fails, fill all with 1
629         if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
630         {
631                 float extraspacing = 0;
632                 const char *p = widthbuf;
633                 int ch = 0;
634
635                 while(ch < 256)
636                 {
637                         if(!COM_ParseToken_Simple(&p, false, false))
638                                 return;
639
640                         if(!strcmp(com_token, "extraspacing"))
641                         {
642                                 if(!COM_ParseToken_Simple(&p, false, false))
643                                         return;
644                                 extraspacing = atof(com_token);
645                         }
646                         else
647                                 fnt->width_of[ch++] = atof(com_token) + extraspacing;
648                 }
649
650                 Mem_Free(widthbuf);
651         }
652
653         maxwidth = fnt->width_of[1];
654         for(i = 2; i < 256; ++i)
655                 maxwidth = max(maxwidth, fnt->width_of[i]);
656         fnt->width_of[0] = maxwidth;
657
658         if(fnt == FONT_CONSOLE)
659                 con_linewidth = -1; // rewrap console in next frame
660 }
661
662 static dp_font_t *FindFont(const char *title)
663 {
664         int i;
665         for(i = 0; i < MAX_FONTS; ++i)
666                 if(!strcmp(dp_fonts[i].title, title))
667                         return &dp_fonts[i];
668         return NULL;
669 }
670
671 static void LoadFont_f(void)
672 {
673         dp_font_t *f;
674         int i;
675         if(Cmd_Argc() < 2)
676         {
677                 Con_Printf("Available font commands:\n");
678                 for(i = 0; i < MAX_FONTS; ++i)
679                         Con_Printf("  loadfont %s gfx/tgafile\n", dp_fonts[i].title);
680                 return;
681         }
682         f = FindFont(Cmd_Argv(1));
683         if(f == NULL)
684         {
685                 Con_Printf("font function not found\n");
686                 return;
687         }
688         LoadFont(true, (Cmd_Argc() < 3) ? "gfx/conchars" : Cmd_Argv(2), f);
689 }
690
691 /*
692 ===============
693 Draw_Init
694 ===============
695 */
696 static void gl_draw_start(void)
697 {
698         int i;
699         drawtexturepool = R_AllocTexturePool();
700
701         numcachepics = 0;
702         memset(cachepichash, 0, sizeof(cachepichash));
703
704         for(i = 0; i < MAX_FONTS; ++i)
705                 LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i]);
706
707         // draw the loading screen so people have something to see in the newly opened window
708         SCR_UpdateLoadingScreen(true);
709 }
710
711 static void gl_draw_shutdown(void)
712 {
713         R_FreeTexturePool(&drawtexturepool);
714
715         numcachepics = 0;
716         memset(cachepichash, 0, sizeof(cachepichash));
717 }
718
719 static void gl_draw_newmap(void)
720 {
721 }
722
723 void GL_Draw_Init (void)
724 {
725         int i, j;
726         Cvar_RegisterVariable(&r_textshadow);
727         Cvar_RegisterVariable(&r_textbrightness);
728         Cvar_RegisterVariable(&r_textcontrast);
729         Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
730         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
731
732         strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
733                 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
734         strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
735         strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
736         strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
737         strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
738         strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
739         strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
740         strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
741         for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
742                 if(!FONT_USER[i].title[0])
743                         dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++);
744 }
745
746 void _DrawQ_Setup(void)
747 {
748         if (r_refdef.draw2dstage)
749                 return;
750         r_refdef.draw2dstage = true;
751         CHECKGLERROR
752         qglViewport(r_refdef.view.x, vid.height - (r_refdef.view.y + r_refdef.view.height), r_refdef.view.width, r_refdef.view.height);CHECKGLERROR
753         GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
754         GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
755         qglDepthFunc(GL_LEQUAL);CHECKGLERROR
756         qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
757         GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
758         R_Mesh_Matrix(&identitymatrix);
759
760         GL_DepthMask(true);
761         GL_DepthRange(0, 1);
762         GL_PolygonOffset(0, 0);
763         GL_DepthTest(false);
764         GL_Color(1,1,1,1);
765         GL_AlphaTest(false);
766         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
767
768         R_SetupGenericShader(true);
769 }
770
771 static void _DrawQ_ProcessDrawFlag(int flags)
772 {
773         _DrawQ_Setup();
774         CHECKGLERROR
775         if(flags == DRAWFLAG_ADDITIVE)
776                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
777         else if(flags == DRAWFLAG_MODULATE)
778                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
779         else if(flags == DRAWFLAG_2XMODULATE)
780                 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
781         else
782                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
783 }
784
785 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
786 {
787         float floats[20];
788
789         _DrawQ_ProcessDrawFlag(flags);
790         GL_Color(red, green, blue, alpha);
791
792         R_Mesh_VertexPointer(floats, 0, 0);
793         R_Mesh_ColorPointer(NULL, 0, 0);
794         R_Mesh_ResetTextureState();
795         R_SetupGenericShader(pic != NULL);
796         if (pic)
797         {
798                 if (width == 0)
799                         width = pic->width;
800                 if (height == 0)
801                         height = pic->height;
802                 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
803                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
804
805 #if 1
806                 floats[12] = 0.0f;floats[13] = 0.0f;
807                 floats[14] = 1.0f;floats[15] = 0.0f;
808                 floats[16] = 1.0f;floats[17] = 1.0f;
809                 floats[18] = 0.0f;floats[19] = 1.0f;
810 #else
811       // AK07: lets be texel correct on the corners
812       {
813          float horz_offset = 0.5f / pic->width;
814          float vert_offset = 0.5f / pic->height;
815
816                    floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
817                    floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
818                    floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
819                    floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
820       }
821 #endif
822         }
823
824         floats[2] = floats[5] = floats[8] = floats[11] = 0;
825         floats[0] = floats[9] = x;
826         floats[1] = floats[4] = y;
827         floats[3] = floats[6] = x + width;
828         floats[7] = floats[10] = y + height;
829
830         R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
831 }
832
833 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
834 {
835         float floats[12];
836
837         _DrawQ_ProcessDrawFlag(flags);
838         GL_Color(red, green, blue, alpha);
839
840         R_Mesh_VertexPointer(floats, 0, 0);
841         R_Mesh_ColorPointer(NULL, 0, 0);
842         R_Mesh_ResetTextureState();
843         R_SetupGenericShader(false);
844
845         floats[2] = floats[5] = floats[8] = floats[11] = 0;
846         floats[0] = floats[9] = x;
847         floats[1] = floats[4] = y;
848         floats[3] = floats[6] = x + width;
849         floats[7] = floats[10] = y + height;
850
851         R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
852 }
853
854 // color tag printing
855 static const vec4_t string_colors[] =
856 {
857         // Quake3 colors
858         // LordHavoc: why on earth is cyan before magenta in Quake3?
859         // LordHavoc: note: Doom3 uses white for [0] and [7]
860         {0.0, 0.0, 0.0, 1.0}, // black
861         {1.0, 0.0, 0.0, 1.0}, // red
862         {0.0, 1.0, 0.0, 1.0}, // green
863         {1.0, 1.0, 0.0, 1.0}, // yellow
864         {0.0, 0.0, 1.0, 1.0}, // blue
865         {0.0, 1.0, 1.0, 1.0}, // cyan
866         {1.0, 0.0, 1.0, 1.0}, // magenta
867         {1.0, 1.0, 1.0, 1.0}, // white
868         // [515]'s BX_COLOREDTEXT extension
869         {1.0, 1.0, 1.0, 0.5}, // half transparent
870         {0.5, 0.5, 0.5, 1.0}  // half brightness
871         // Black's color table
872         //{1.0, 1.0, 1.0, 1.0},
873         //{1.0, 0.0, 0.0, 1.0},
874         //{0.0, 1.0, 0.0, 1.0},
875         //{0.0, 0.0, 1.0, 1.0},
876         //{1.0, 1.0, 0.0, 1.0},
877         //{0.0, 1.0, 1.0, 1.0},
878         //{1.0, 0.0, 1.0, 1.0},
879         //{0.1, 0.1, 0.1, 1.0}
880 };
881
882 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
883
884 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
885 {
886         float C = r_textcontrast.value;
887         float B = r_textbrightness.value;
888         Vector4Copy(string_colors[colorindex], color);
889         Vector4Set(color, (color[0] * C + B) * r, (color[1] * C + B) * g, (color[2] * C + B) * b, color[3] * a);
890         if (shadow)
891         {
892                 float shadowalpha = color[0]+color[1]+color[2] * 0.8;
893                 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
894         }
895 }
896
897 float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
898 {
899         int num, colorindex = STRING_COLOR_DEFAULT;
900         size_t i;
901         float x = 0;
902
903         if (*maxlen < 1)
904                 *maxlen = 1<<30;
905
906         if (!outcolor || *outcolor == -1)
907                 colorindex = STRING_COLOR_DEFAULT;
908         else
909                 colorindex = *outcolor;
910
911         for (i = 0;i < *maxlen && text[i];i++)
912         {
913                 if (text[i] == ' ')
914                 {
915                         if(x + fnt->width_of[' '] > maxwidth)
916                                 break; // oops, can't draw this
917                         x += fnt->width_of[' '];
918                         continue;
919                 }
920                 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen)
921                 {
922                         if (text[i+1] == STRING_COLOR_TAG)
923                         {
924                                 i++;
925                         }
926                         else if (text[i+1] >= '0' && text[i+1] <= '9')
927                         {
928                                 colorindex = text[i+1] - '0';
929                                 i++;
930                                 continue;
931                         }
932                 }
933                 num = (unsigned char) text[i];
934                 if(x + fnt->width_of[num] > maxwidth)
935                         break; // oops, can't draw this
936                 x += fnt->width_of[num];
937         }
938
939         *maxlen = i;
940
941         if (outcolor)
942                 *outcolor = colorindex;
943
944         return x;
945 }
946
947 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)
948 {
949         int num, shadow, colorindex = STRING_COLOR_DEFAULT;
950         size_t i;
951         float x = startx, y, s, t, u, v, thisw;
952         float *av, *at, *ac;
953         float color[4];
954         int batchcount;
955         float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
956         float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
957         float color4f[QUADELEMENTS_MAXQUADS*4*4];
958
959         int tw, th;
960         tw = R_TextureWidth(fnt->tex);
961         th = R_TextureHeight(fnt->tex);
962
963         if (maxlen < 1)
964                 maxlen = 1<<30;
965
966         _DrawQ_ProcessDrawFlag(flags);
967
968         R_Mesh_ColorPointer(color4f, 0, 0);
969         R_Mesh_ResetTextureState();
970         R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
971         R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
972         R_Mesh_VertexPointer(vertex3f, 0, 0);
973         R_SetupGenericShader(true);
974
975         ac = color4f;
976         at = texcoord2f;
977         av = vertex3f;
978         batchcount = 0;
979
980         for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
981         {
982                 if (!outcolor || *outcolor == -1)
983                         colorindex = STRING_COLOR_DEFAULT;
984                 else
985                         colorindex = *outcolor;
986                 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
987
988                 x = startx;
989                 y = starty;
990                 if (shadow)
991                 {
992                         x += r_textshadow.value;
993                         y += r_textshadow.value;
994                 }
995                 for (i = 0;i < maxlen && text[i];i++)
996                 {
997                         if (text[i] == ' ')
998                         {
999                                 x += fnt->width_of[' '] * w;
1000                                 continue;
1001                         }
1002                         if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen)
1003                         {
1004                                 if (text[i+1] == STRING_COLOR_TAG)
1005                                 {
1006                                         i++;
1007                                 }
1008                                 else if (text[i+1] >= '0' && text[i+1] <= '9')
1009                                 {
1010                                         colorindex = text[i+1] - '0';
1011                                         DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
1012                                         i++;
1013                                         continue;
1014                                 }
1015                         }
1016                         num = (unsigned char) text[i];
1017                         thisw = fnt->width_of[num];
1018                         // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1019                         s = (num & 15)*0.0625f + (0.5f / tw);
1020                         t = (num >> 4)*0.0625f + (0.5f / th);
1021                         u = 0.0625f * thisw - (1.0f / tw);
1022                         v = 0.0625f - (1.0f / th);
1023                         ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1024                         ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1025                         ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1026                         ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1027                         at[ 0] = s  ;at[ 1] = t  ;
1028                         at[ 2] = s+u;at[ 3] = t  ;
1029                         at[ 4] = s+u;at[ 5] = t+v;
1030                         at[ 6] = s  ;at[ 7] = t+v;
1031                         av[ 0] = x  ;av[ 1] = y  ;av[ 2] = 10;
1032                         av[ 3] = x+w*thisw;av[ 4] = y  ;av[ 5] = 10;
1033                         av[ 6] = x+w*thisw;av[ 7] = y+h;av[ 8] = 10;
1034                         av[ 9] = x  ;av[10] = y+h;av[11] = 10;
1035                         ac += 16;
1036                         at += 8;
1037                         av += 12;
1038                         batchcount++;
1039                         if (batchcount >= QUADELEMENTS_MAXQUADS)
1040                         {
1041                                 GL_LockArrays(0, batchcount * 4);
1042                                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, NULL, quadelements, 0, 0);
1043                                 GL_LockArrays(0, 0);
1044                                 batchcount = 0;
1045                                 ac = color4f;
1046                                 at = texcoord2f;
1047                                 av = vertex3f;
1048                         }
1049                         x += thisw * w;
1050                 }
1051         }
1052         if (batchcount > 0)
1053         {
1054                 GL_LockArrays(0, batchcount * 4);
1055                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, NULL, quadelements, 0, 0);
1056                 GL_LockArrays(0, 0);
1057         }
1058
1059         if (outcolor)
1060                 *outcolor = colorindex;
1061
1062         // note: this relies on the proper text (not shadow) being drawn last
1063         return x;
1064 }
1065
1066 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)
1067 {
1068         return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]);
1069 }
1070
1071 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1072 {
1073         return DrawQ_TextWidth_Font_UntilWidth_TrackColors(text, maxlen, NULL, ignorecolorcodes, fnt, maxWidth);
1074 }
1075
1076 float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt)
1077 {
1078         return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, ignorecolorcodes, fnt, 1000000000);
1079 }
1080
1081 #if 0
1082 // not used
1083 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1084 {
1085         int color, numchars = 0;
1086         char *outputend2c = output2c + maxoutchars - 2;
1087         if (!outcolor || *outcolor == -1)
1088                 color = STRING_COLOR_DEFAULT;
1089         else
1090                 color = *outcolor;
1091         if (!maxreadchars)
1092                 maxreadchars = 1<<30;
1093         textend = text + maxreadchars;
1094         while (text != textend && *text)
1095         {
1096                 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1097                 {
1098                         if (text[1] == STRING_COLOR_TAG)
1099                                 text++;
1100                         else if (text[1] >= '0' && text[1] <= '9')
1101                         {
1102                                 color = text[1] - '0';
1103                                 text += 2;
1104                                 continue;
1105                         }
1106                 }
1107                 if (output2c >= outputend2c)
1108                         break;
1109                 *output2c++ = *text++;
1110                 *output2c++ = color;
1111                 numchars++;
1112         }
1113         output2c[0] = output2c[1] = 0;
1114         if (outcolor)
1115                 *outcolor = color;
1116         return numchars;
1117 }
1118 #endif
1119
1120 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)
1121 {
1122         float floats[36];
1123
1124         _DrawQ_ProcessDrawFlag(flags);
1125
1126         R_Mesh_VertexPointer(floats, 0, 0);
1127         R_Mesh_ColorPointer(floats + 20, 0, 0);
1128         R_Mesh_ResetTextureState();
1129         R_SetupGenericShader(pic != NULL);
1130         if (pic)
1131         {
1132                 if (width == 0)
1133                         width = pic->width;
1134                 if (height == 0)
1135                         height = pic->height;
1136                 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1137                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1138                 floats[12] = s1;floats[13] = t1;
1139                 floats[14] = s2;floats[15] = t2;
1140                 floats[16] = s4;floats[17] = t4;
1141                 floats[18] = s3;floats[19] = t3;
1142         }
1143
1144         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1145         floats[0] = floats[9] = x;
1146         floats[1] = floats[4] = y;
1147         floats[3] = floats[6] = x + width;
1148         floats[7] = floats[10] = y + height;
1149         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1150         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1151         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1152         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1153
1154         R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
1155 }
1156
1157 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1158 {
1159         _DrawQ_ProcessDrawFlag(flags);
1160
1161         R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1162         R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1163         R_Mesh_ResetTextureState();
1164         R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
1165         R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1166         R_SetupGenericShader(mesh->texture != NULL);
1167
1168         GL_LockArrays(0, mesh->num_vertices);
1169         R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, NULL, mesh->data_element3s, 0, 0);
1170         GL_LockArrays(0, 0);
1171 }
1172
1173 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1174 {
1175         int num;
1176
1177         _DrawQ_ProcessDrawFlag(flags);
1178
1179         GL_Color(1,1,1,1);
1180         CHECKGLERROR
1181         qglBegin(GL_LINE_LOOP);
1182         for (num = 0;num < mesh->num_vertices;num++)
1183         {
1184                 if (mesh->data_color4f)
1185                         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]);
1186                 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1187         }
1188         qglEnd();
1189         CHECKGLERROR
1190 }
1191
1192 //[515]: this is old, delete
1193 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1194 {
1195         _DrawQ_ProcessDrawFlag(flags);
1196
1197         R_SetupGenericShader(false);
1198
1199         CHECKGLERROR
1200         qglLineWidth(width);CHECKGLERROR
1201
1202         GL_Color(r,g,b,alpha);
1203         CHECKGLERROR
1204         qglBegin(GL_LINES);
1205         qglVertex2f(x1, y1);
1206         qglVertex2f(x2, y2);
1207         qglEnd();
1208         CHECKGLERROR
1209 }
1210
1211 void DrawQ_SetClipArea(float x, float y, float width, float height)
1212 {
1213         _DrawQ_Setup();
1214
1215         // We have to convert the con coords into real coords
1216         // OGL uses top to bottom
1217         GL_Scissor((int)(x * ((float)vid.width / vid_conwidth.integer)), (int)(y * ((float) vid.height / vid_conheight.integer)), (int)(width * ((float)vid.width / vid_conwidth.integer)), (int)(height * ((float)vid.height / vid_conheight.integer)));
1218
1219         GL_ScissorTest(true);
1220 }
1221
1222 void DrawQ_ResetClipArea(void)
1223 {
1224         _DrawQ_Setup();
1225         GL_ScissorTest(false);
1226 }
1227
1228 void DrawQ_Finish(void)
1229 {
1230         r_refdef.draw2dstage = false;
1231 }
1232
1233 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1234 void R_DrawGamma(void)
1235 {
1236         float c[4];
1237         if (!vid_usinghwgamma && !(r_glsl.integer && v_glslgamma.integer))
1238         {
1239                 // all the blends ignore depth
1240                 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1241                 R_Mesh_ColorPointer(NULL, 0, 0);
1242                 R_Mesh_ResetTextureState();
1243                 R_SetupGenericShader(false);
1244                 GL_DepthMask(true);
1245                 GL_DepthRange(0, 1);
1246                 GL_PolygonOffset(0, 0);
1247                 GL_DepthTest(false);
1248                 if (v_color_enable.integer)
1249                 {
1250                         c[0] = v_color_white_r.value;
1251                         c[1] = v_color_white_g.value;
1252                         c[2] = v_color_white_b.value;
1253                 }
1254                 else
1255                         c[0] = c[1] = c[2] = v_contrast.value;
1256                 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1257                 {
1258                         GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1259                         while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1260                         {
1261                                 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1262                                 R_Mesh_Draw(0, 3, 0, 1, NULL, polygonelements, 0, 0);
1263                                 VectorScale(c, 0.5, c);
1264                         }
1265                 }
1266                 if (v_color_enable.integer)
1267                 {
1268                         c[0] = v_color_black_r.value;
1269                         c[1] = v_color_black_g.value;
1270                         c[2] = v_color_black_b.value;
1271                 }
1272                 else
1273                         c[0] = c[1] = c[2] = v_brightness.value;
1274                 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1275                 {
1276                         GL_BlendFunc(GL_ONE, GL_ONE);
1277                         GL_Color(c[0], c[1], c[2], 1);
1278                         R_Mesh_Draw(0, 3, 0, 1, NULL, polygonelements, 0, 0);
1279                 }
1280         }
1281 }
1282