]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - gl_draw.c
increased server browser viewlist limit (was 128) to match the serverlist limit ...
[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
27
28 static rtexture_t *char_texture;
29
30 //=============================================================================
31 /* Support Routines */
32
33 #define MAX_CACHED_PICS 256
34 #define CACHEPICHASHSIZE 256
35 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
36 static cachepic_t cachepics[MAX_CACHED_PICS];
37 static int numcachepics;
38
39 static rtexturepool_t *drawtexturepool;
40
41 static qbyte concharimage[13468] =
42 {
43 #include "lhfont.h"
44 };
45
46 extern qbyte *LoadTGA (qbyte *f, int matchwidth, int matchheight);
47
48 static rtexture_t *draw_generateconchars(void)
49 {
50         int i;
51         qbyte buffer[65536][4], *data = NULL;
52         double random;
53
54         fs_filesize = 13396;
55         data = LoadTGA (concharimage, 256, 256);
56         fs_filesize = -1;
57 // Gold numbers
58         for (i = 0;i < 8192;i++)
59         {
60                 random = lhrandom (0.0,1.0);
61                 buffer[i][0] = 83 + (qbyte)(random * 64);
62                 buffer[i][1] = 71 + (qbyte)(random * 32);
63                 buffer[i][2] = 23 + (qbyte)(random * 16);
64                 buffer[i][3] = data[i*4+0];
65         }
66 // White chars
67         for (i = 8192;i < 32768;i++)
68         {
69                 random = lhrandom (0.0,1.0);
70                 buffer[i][0] = 95 + (qbyte)(random * 64);
71                 buffer[i][1] = 95 + (qbyte)(random * 64);
72                 buffer[i][2] = 95 + (qbyte)(random * 64);
73                 buffer[i][3] = data[i*4+0];
74         }
75 // Gold numbers
76         for (i = 32768;i < 40960;i++)
77         {
78                 random = lhrandom (0.0,1.0);
79                 buffer[i][0] = 83 + (qbyte)(random * 64);
80                 buffer[i][1] = 71 + (qbyte)(random * 32);
81                 buffer[i][2] = 23 + (qbyte)(random * 16);
82                 buffer[i][3] = data[i*4+0];
83         }
84 // Red chars
85         for (i = 40960;i < 65536;i++)
86         {
87                 random = lhrandom (0.0,1.0);
88                 buffer[i][0] = 96 + (qbyte)(random * 64);
89                 buffer[i][1] = 43 + (qbyte)(random * 32);
90                 buffer[i][2] = 27 + (qbyte)(random * 32);
91                 buffer[i][3] = data[i*4+0];
92         }
93
94 #if 0
95         Image_WriteTGARGBA ("gfx/generated_conchars.tga", 256, 256, &buffer[0][0]);
96 #endif
97
98         Mem_Free(data);
99         return R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, &buffer[0][0], TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
100 }
101
102 static qbyte pointerimage[256] =
103 {
104         "333333332......."
105         "26777761........"
106         "2655541........."
107         "265541.........."
108         "2654561........."
109         "26414561........"
110         "251.14561......."
111         "21...14561......"
112         "1.....141......."
113         ".......1........"
114         "................"
115         "................"
116         "................"
117         "................"
118         "................"
119         "................"
120 };
121
122 static rtexture_t *draw_generatemousepointer(void)
123 {
124         int i;
125         qbyte buffer[256][4];
126         for (i = 0;i < 256;i++)
127         {
128                 if (pointerimage[i] == '.')
129                 {
130                         buffer[i][0] = 0;
131                         buffer[i][1] = 0;
132                         buffer[i][2] = 0;
133                         buffer[i][3] = 0;
134                 }
135                 else
136                 {
137                         buffer[i][0] = (pointerimage[i] - '0') * 16;
138                         buffer[i][1] = (pointerimage[i] - '0') * 16;
139                         buffer[i][2] = (pointerimage[i] - '0') * 16;
140                         buffer[i][3] = 255;
141                 }
142         }
143         return R_LoadTexture2D(drawtexturepool, "mousepointer", 16, 16, &buffer[0][0], TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
144 }
145
146 // must match NUMCROSSHAIRS in r_crosshairs.c
147 #define NUMCROSSHAIRS 5
148
149 static qbyte *crosshairtexdata[NUMCROSSHAIRS] =
150 {
151         "................"
152         "................"
153         "................"
154         "...33......33..."
155         "...355....553..."
156         "....577..775...."
157         ".....77..77....."
158         "................"
159         "................"
160         ".....77..77....."
161         "....577..775...."
162         "...355....553..."
163         "...33......33..."
164         "................"
165         "................"
166         "................"
167         ,
168         "................"
169         "................"
170         "................"
171         "...3........3..."
172         "....5......5...."
173         ".....7....7....."
174         "......7..7......"
175         "................"
176         "................"
177         "......7..7......"
178         ".....7....7....."
179         "....5......5...."
180         "...3........3..."
181         "................"
182         "................"
183         "................"
184         ,
185         "................"
186         ".......77......."
187         ".......77......."
188         "................"
189         "................"
190         ".......44......."
191         ".......44......."
192         ".77..44..44..77."
193         ".77..44..44..77."
194         ".......44......."
195         ".......44......."
196         "................"
197         ".......77......."
198         ".......77......."
199         "................"
200         "................"
201         ,
202         "................"
203         "................"
204         "................"
205         "................"
206         "................"
207         "................"
208         "................"
209         "................"
210         "........7777777."
211         "........752....."
212         "........72......"
213         "........7......."
214         "........7......."
215         "........7......."
216         "................"
217         "................"
218         ,
219         "................"
220         "................"
221         "................"
222         "................"
223         "................"
224         "........7......."
225         "................"
226         "........4......."
227         ".....7.4.4.7...."
228         "........4......."
229         "................"
230         "........7......."
231         "................"
232         "................"
233         "................"
234         "................"
235 };
236
237 static rtexture_t *draw_generatecrosshair(int num)
238 {
239         int i;
240         char *in;
241         qbyte data[16*16][4];
242         in = crosshairtexdata[num];
243         for (i = 0;i < 16*16;i++)
244         {
245                 if (in[i] == '.')
246                 {
247                         data[i][0] = 255;
248                         data[i][1] = 255;
249                         data[i][2] = 255;
250                         data[i][3] = 0;
251                 }
252                 else
253                 {
254                         data[i][0] = 255;
255                         data[i][1] = 255;
256                         data[i][2] = 255;
257                         data[i][3] = (qbyte) ((int) (in[i] - '0') * 255 / 7);
258                 }
259         }
260         return R_LoadTexture2D(drawtexturepool, va("crosshair%i", num), 16, 16, &data[0][0], TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
261 }
262
263 static rtexture_t *draw_generateditherpattern(void)
264 {
265 #if 1
266         int x, y;
267         qbyte data[8*8*4];
268         for (y = 0;y < 8;y++)
269         {
270                 for (x = 0;x < 8;x++)
271                 {
272                         data[(y*8+x)*4+0] = data[(y*8+x)*4+1] = data[(y*8+x)*4+2] = ((x^y) & 4) ? 255 : 0;
273                         data[(y*8+x)*4+3] = 255;
274                 }
275         }
276         return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, data, TEXTYPE_RGBA, TEXF_FORCENEAREST | TEXF_PRECACHE, NULL);
277 #else
278         qbyte data[16];
279         memset(data, 255, sizeof(data));
280         data[0] = data[1] = data[2] = data[12] = data[13] = data[14] = 0;
281         return R_LoadTexture2D(drawtexturepool, "ditherpattern", 2, 2, data, TEXTYPE_RGBA, TEXF_FORCENEAREST | TEXF_PRECACHE, NULL);
282 #endif
283 }
284
285 /*
286 ================
287 Draw_CachePic
288 ================
289 */
290 // FIXME: move this to client somehow
291 cachepic_t      *Draw_CachePic (const char *path, qboolean persistent)
292 {
293         int i, crc, hashkey;
294         cachepic_t *pic;
295         qpic_t *p;
296         int flags;
297
298         if (!strncmp(CLVIDEOPREFIX, path, sizeof(CLVIDEOPREFIX) - 1)) {
299                 clvideo_t *video;
300
301                 video = CL_GetVideo(path);
302                 if( video )
303                         return &video->cpif;
304         }
305
306         crc = CRC_Block(path, strlen(path));
307         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
308         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
309                 if (!strcmp (path, pic->name))
310                         return pic;
311
312         if (numcachepics == MAX_CACHED_PICS)
313         {
314                 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS");
315                 // FIXME: support NULL in callers?
316                 return cachepics; // return the first one
317         }
318         pic = cachepics + (numcachepics++);
319         strlcpy (pic->name, path, sizeof(pic->name));
320         // link into list
321         pic->chain = cachepichash[hashkey];
322         cachepichash[hashkey] = pic;
323
324         flags = TEXF_ALPHA;
325         if (persistent)
326                 flags |= TEXF_PRECACHE;
327         if (!strcmp(path, "gfx/colorcontrol/ditherpattern.tga"))
328                 flags |= TEXF_CLAMP;
329
330         // load the pic from disk
331         pic->tex = loadtextureimage(drawtexturepool, path, 0, 0, false, flags);
332         if (pic->tex == NULL && !strncmp(path, "gfx/", 4))
333         {
334                 // compatibility with older versions
335                 pic->tex = loadtextureimage(drawtexturepool, path + 4, 0, 0, false, flags);
336                 // failed to find gfx/whatever.tga or similar, try the wad
337                 if (pic->tex == NULL && (p = W_GetLumpName (path + 4)))
338                 {
339                         if (!strcmp(path, "gfx/conchars"))
340                         {
341                                 qbyte *pix;
342                                 // conchars is a raw image and with the wrong transparent color
343                                 pix = (qbyte *)p;
344                                 for (i = 0;i < 128 * 128;i++)
345                                         if (pix[i] == 0)
346                                                 pix[i] = 255;
347                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, pix, TEXTYPE_PALETTE, flags, palette_complete);
348                         }
349                         else
350                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, p->width, p->height, p->data, TEXTYPE_PALETTE, flags, palette_complete);
351                 }
352         }
353
354         if (pic->tex == NULL && !strcmp(path, "gfx/conchars"))
355                 pic->tex = draw_generateconchars();
356         if (pic->tex == NULL && !strcmp(path, "ui/mousepointer.tga"))
357                 pic->tex = draw_generatemousepointer();
358         if (pic->tex == NULL && !strcmp(path, "gfx/prydoncursor001.tga"))
359                 pic->tex = draw_generatemousepointer();
360         if (pic->tex == NULL && !strcmp(path, "gfx/crosshair1.tga"))
361                 pic->tex = draw_generatecrosshair(0);
362         if (pic->tex == NULL && !strcmp(path, "gfx/crosshair2.tga"))
363                 pic->tex = draw_generatecrosshair(1);
364         if (pic->tex == NULL && !strcmp(path, "gfx/crosshair3.tga"))
365                 pic->tex = draw_generatecrosshair(2);
366         if (pic->tex == NULL && !strcmp(path, "gfx/crosshair4.tga"))
367                 pic->tex = draw_generatecrosshair(3);
368         if (pic->tex == NULL && !strcmp(path, "gfx/crosshair5.tga"))
369                 pic->tex = draw_generatecrosshair(4);
370         if (pic->tex == NULL && !strcmp(path, "gfx/colorcontrol/ditherpattern.tga"))
371                 pic->tex = draw_generateditherpattern();
372         if (pic->tex == NULL)
373         {
374                 Con_Printf("Draw_CachePic: failed to load %s\n", path);
375                 pic->tex = r_texture_notexture;
376         }
377
378         pic->width = R_TextureWidth(pic->tex);
379         pic->height = R_TextureHeight(pic->tex);
380         return pic;
381 }
382
383 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, qbyte *pixels)
384 {
385         int crc, hashkey;
386         cachepic_t *pic;
387
388         crc = CRC_Block(picname, strlen(picname));
389         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
390         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
391                 if (!strcmp (picname, pic->name))
392                         break;
393
394         if (pic)
395         {
396                 if (pic->tex && pic->width == width && pic->height == height)
397                 {
398                         R_UpdateTexture(pic->tex, pixels);
399                         return pic;
400                 }
401         }
402         else
403         {
404                 if (pic == NULL)
405                 {
406                         if (numcachepics == MAX_CACHED_PICS)
407                         {
408                                 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS");
409                                 // FIXME: support NULL in callers?
410                                 return cachepics; // return the first one
411                         }
412                         pic = cachepics + (numcachepics++);
413                         strcpy (pic->name, picname);
414                         // link into list
415                         pic->chain = cachepichash[hashkey];
416                         cachepichash[hashkey] = pic;
417                 }
418         }
419
420         pic->width = width;
421         pic->height = height;
422         if (pic->tex)
423                 R_FreeTexture(pic->tex);
424         pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels, TEXTYPE_RGBA, alpha ? TEXF_ALPHA : 0, NULL);
425         return pic;
426 }
427
428 void Draw_FreePic(const char *picname)
429 {
430         int crc;
431         int hashkey;
432         cachepic_t *pic;
433         // this doesn't really free the pic, but does free it's texture
434         crc = CRC_Block(picname, strlen(picname));
435         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
436         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
437         {
438                 if (!strcmp (picname, pic->name) && pic->tex)
439                 {
440                         R_FreeTexture(pic->tex);
441                         pic->width = 0;
442                         pic->height = 0;
443                         return;
444                 }
445         }
446 }
447
448 /*
449 ===============
450 Draw_Init
451 ===============
452 */
453 static void gl_draw_start(void)
454 {
455         drawtexturepool = R_AllocTexturePool();
456
457         numcachepics = 0;
458         memset(cachepichash, 0, sizeof(cachepichash));
459
460         char_texture = Draw_CachePic("gfx/conchars", true)->tex;
461 }
462
463 static void gl_draw_shutdown(void)
464 {
465         R_FreeTexturePool(&drawtexturepool);
466
467         numcachepics = 0;
468         memset(cachepichash, 0, sizeof(cachepichash));
469 }
470
471 static void gl_draw_newmap(void)
472 {
473 }
474
475 void GL_Draw_Init (void)
476 {
477         numcachepics = 0;
478         memset(cachepichash, 0, sizeof(cachepichash));
479
480         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
481 }
482
483 float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
484
485 int quadelements[768];
486 void R_DrawQueue(void)
487 {
488         int pos, num, chartexnum, texnum, batch;
489         float x, y, w, h, s, t, u, v, *av, *at, c[4];
490         cachepic_t *pic;
491         drawqueue_t *dq;
492         char *str;
493         int batchcount;
494         unsigned int color;
495         drawqueuemesh_t *mesh;
496         rmeshstate_t m;
497
498         if (!r_render.integer)
499                 return;
500
501         if (!quadelements[1])
502         {
503                 // elements for rendering a series of quads as triangles
504                 for (batch = 0, pos = 0, num = 0;batch < 128;batch++, num += 4)
505                 {
506                         quadelements[pos++] = num;
507                         quadelements[pos++] = num + 1;
508                         quadelements[pos++] = num + 2;
509                         quadelements[pos++] = num;
510                         quadelements[pos++] = num + 2;
511                         quadelements[pos++] = num + 3;
512                 }
513         }
514
515         r_view_width = bound(0, r_refdef.width, vid.width);
516         r_view_height = bound(0, r_refdef.height, vid.height);
517         r_view_depth = 1;
518         r_view_x = bound(0, r_refdef.x, vid.width - r_refdef.width);
519         r_view_y = bound(0, r_refdef.y, vid.height - r_refdef.height);
520         r_view_z = 0;
521         r_view_fov_x = bound(0.1, r_refdef.fov_x, 170);
522         r_view_fov_y = bound(0.1, r_refdef.fov_y, 170);
523         r_view_matrix = r_refdef.viewentitymatrix;
524         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
525
526         qglViewport(r_view_x, vid.height - (r_view_y + r_view_height), r_view_width, r_view_height);
527         GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
528         qglDepthFunc(GL_LEQUAL);
529         R_Mesh_Matrix(&r_identitymatrix);
530
531         chartexnum = R_GetTexture(char_texture);
532
533         memset(&m, 0, sizeof(m));
534
535         pic = NULL;
536         texnum = 0;
537         color = 0;
538         GL_Color(1,1,1,1);
539
540         batch = false;
541         batchcount = 0;
542         for (pos = 0;pos < r_refdef.drawqueuesize;pos += ((drawqueue_t *)(r_refdef.drawqueue + pos))->size)
543         {
544                 dq = (drawqueue_t *)(r_refdef.drawqueue + pos);
545                 color = dq->color;
546
547                 if(dq->flags == DRAWFLAG_ADDITIVE)
548                         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
549                 else if(dq->flags == DRAWFLAG_MODULATE)
550                         GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
551                 else if(dq->flags == DRAWFLAG_2XMODULATE)
552                         GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
553                 else
554                         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
555
556                 GL_DepthMask(true);
557                 GL_DepthTest(false);
558
559                 c[0] = (float) ((color >> 24) & 0xFF) * (1.0f / 255.0f);
560                 c[1] = (float) ((color >> 16) & 0xFF) * (1.0f / 255.0f);
561                 c[2] = (float) ((color >>  8) & 0xFF) * (1.0f / 255.0f);
562                 c[3] = (float) ( color        & 0xFF) * (1.0f / 255.0f);
563                 x = dq->x;
564                 y = dq->y;
565                 w = dq->scalex;
566                 h = dq->scaley;
567
568                 switch(dq->command)
569                 {
570                 case DRAWQUEUE_STRING:
571                         GL_Color(c[0], c[1], c[2], c[3]);
572                         str = (char *)(dq + 1);
573                         batchcount = 0;
574                         m.pointer_vertex = varray_vertex3f;
575                         m.pointer_color = NULL;
576                         m.pointer_texcoord[0] = varray_texcoord2f[0];
577                         m.tex[0] = chartexnum;
578                         R_Mesh_State(&m);
579                         at = varray_texcoord2f[0];
580                         av = varray_vertex3f;
581                         while ((num = *str++) && x < vid_conwidth.integer)
582                         {
583                                 if (num != ' ')
584                                 {
585                                         s = (num & 15)*0.0625f + (0.5f / 256.0f);
586                                         t = (num >> 4)*0.0625f + (0.5f / 256.0f);
587                                         u = 0.0625f - (1.0f / 256.0f);
588                                         v = 0.0625f - (1.0f / 256.0f);
589                                         at[ 0] = s  ;at[ 1] = t  ;
590                                         at[ 2] = s+u;at[ 3] = t  ;
591                                         at[ 4] = s+u;at[ 5] = t+v;
592                                         at[ 6] = s  ;at[ 7] = t+v;
593                                         av[ 0] = x  ;av[ 1] = y  ;av[ 2] = 10;
594                                         av[ 3] = x+w;av[ 4] = y  ;av[ 5] = 10;
595                                         av[ 6] = x+w;av[ 7] = y+h;av[ 8] = 10;
596                                         av[ 9] = x  ;av[10] = y+h;av[11] = 10;
597                                         at += 8;
598                                         av += 12;
599                                         batchcount++;
600                                         if (batchcount >= 128)
601                                         {
602                                                 GL_LockArrays(0, batchcount * 4);
603                                                 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements);
604                                                 GL_LockArrays(0, 0);
605                                                 batchcount = 0;
606                                                 at = varray_texcoord2f[0];
607                                                 av = varray_vertex3f;
608                                         }
609                                 }
610                                 x += w;
611                         }
612                         if (batchcount > 0)
613                         {
614                                 GL_LockArrays(0, batchcount * 4);
615                                 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements);
616                                 GL_LockArrays(0, 0);
617                         }
618                         break;
619                 case DRAWQUEUE_MESH:
620                         mesh = (void *)(dq + 1);
621                         m.pointer_vertex = mesh->data_vertex3f;
622                         m.pointer_color = mesh->data_color4f;
623                         m.pointer_texcoord[0] = mesh->data_texcoord2f;
624                         m.tex[0] = R_GetTexture(mesh->texture);
625                         if (!m.tex[0])
626                                 m.pointer_texcoord[0] = NULL;
627                         R_Mesh_State(&m);
628                         GL_LockArrays(0, mesh->num_vertices);
629                         R_Mesh_Draw(0, mesh->num_vertices, mesh->num_triangles, mesh->data_element3i);
630                         GL_LockArrays(0, 0);
631                         break;
632                 case DRAWQUEUE_SETCLIP:
633                         {
634                                 // We have to convert the con coords into real coords
635                                 int x , y, width, height;
636                                 x = dq->x * ((float)vid.width / vid_conwidth.integer);
637                                 // OGL uses top to bottom
638                                 y = dq->y * ((float) vid.height / vid_conheight.integer);
639                                 width = dq->scalex * ((float)vid.width / vid_conwidth.integer);
640                                 height = dq->scaley * ((float)vid.height / vid_conheight.integer);
641
642                                 GL_Scissor(x, y, width, height);
643
644                                 GL_ScissorTest(true);
645                         }
646                         break;
647                 case DRAWQUEUE_RESETCLIP:
648                         GL_ScissorTest(false);
649                         break;
650                 }
651         }
652
653         if (!vid_usinghwgamma)
654         {
655                 // all the blends ignore depth
656                 memset(&m, 0, sizeof(m));
657                 m.pointer_vertex = blendvertex3f;
658                 R_Mesh_State(&m);
659                 GL_DepthMask(true);
660                 GL_DepthTest(false);
661                 if (v_color_enable.integer)
662                 {
663                         c[0] = v_color_white_r.value;
664                         c[1] = v_color_white_g.value;
665                         c[2] = v_color_white_b.value;
666                 }
667                 else
668                         c[0] = c[1] = c[2] = v_contrast.value;
669                 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
670                 {
671                         GL_BlendFunc(GL_DST_COLOR, GL_ONE);
672                         while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
673                         {
674                                 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
675                                 R_Mesh_Draw(0, 3, 1, polygonelements);
676                                 VectorScale(c, 0.5, c);
677                         }
678                 }
679                 if (v_color_enable.integer)
680                 {
681                         c[0] = v_color_black_r.value;
682                         c[1] = v_color_black_g.value;
683                         c[2] = v_color_black_b.value;
684                 }
685                 else
686                         c[0] = c[1] = c[2] = v_brightness.value;
687                 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
688                 {
689                         GL_BlendFunc(GL_ONE, GL_ONE);
690                         GL_Color(c[0], c[1], c[2], 1);
691                         R_Mesh_Draw(0, 3, 1, polygonelements);
692                 }
693         }
694 }
695