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