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