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