added r_textbrightness to control color codes (so conchars can use black borders...
[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 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)"};
28 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)"};
29
30 static rtexture_t *char_texture;
31 cachepic_t *r_crosshairs[NUMCROSSHAIRS+1];
32
33 //=============================================================================
34 /* Support Routines */
35
36 #define FONT_FILESIZE 13468
37 #define MAX_CACHED_PICS 1024
38 #define CACHEPICHASHSIZE 256
39 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
40 static cachepic_t cachepics[MAX_CACHED_PICS];
41 static int numcachepics;
42
43 static rtexturepool_t *drawtexturepool;
44
45 static unsigned char concharimage[FONT_FILESIZE] =
46 {
47 #include "lhfont.h"
48 };
49
50 static rtexture_t *draw_generateconchars(void)
51 {
52         int i;
53         unsigned char buffer[65536][4], *data = NULL;
54         double random;
55
56         data = LoadTGA (concharimage, FONT_FILESIZE, 256, 256);
57 // Gold numbers
58         for (i = 0;i < 8192;i++)
59         {
60                 random = lhrandom (0.0,1.0);
61                 buffer[i][0] = 83 + (unsigned char)(random * 64);
62                 buffer[i][1] = 71 + (unsigned char)(random * 32);
63                 buffer[i][2] = 23 + (unsigned char)(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 + (unsigned char)(random * 64);
71                 buffer[i][1] = 95 + (unsigned char)(random * 64);
72                 buffer[i][2] = 95 + (unsigned char)(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 + (unsigned char)(random * 64);
80                 buffer[i][1] = 71 + (unsigned char)(random * 32);
81                 buffer[i][2] = 23 + (unsigned char)(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 + (unsigned char)(random * 64);
89                 buffer[i][1] = 43 + (unsigned char)(random * 32);
90                 buffer[i][2] = 27 + (unsigned char)(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 char *pointerimage =
103         "333333332......."
104         "26777761........"
105         "2655541........."
106         "265541.........."
107         "2654561........."
108         "26414561........"
109         "251.14561......."
110         "21...14561......"
111         "1.....141......."
112         ".......1........"
113         "................"
114         "................"
115         "................"
116         "................"
117         "................"
118         "................"
119 ;
120
121 static rtexture_t *draw_generatemousepointer(void)
122 {
123         int i;
124         unsigned char buffer[256][4];
125         for (i = 0;i < 256;i++)
126         {
127                 if (pointerimage[i] == '.')
128                 {
129                         buffer[i][0] = 0;
130                         buffer[i][1] = 0;
131                         buffer[i][2] = 0;
132                         buffer[i][3] = 0;
133                 }
134                 else
135                 {
136                         buffer[i][0] = (pointerimage[i] - '0') * 16;
137                         buffer[i][1] = (pointerimage[i] - '0') * 16;
138                         buffer[i][2] = (pointerimage[i] - '0') * 16;
139                         buffer[i][3] = 255;
140                 }
141         }
142         return R_LoadTexture2D(drawtexturepool, "mousepointer", 16, 16, &buffer[0][0], TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
143 }
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] = data[i][1] = data[i][2] = (unsigned char) ((int) (in[i] - '0') * 127 / 7 + 128);
268                         data[i][3] = 255;
269                 }
270         }
271         return R_LoadTexture2D(drawtexturepool, va("crosshair%i", num+1), 16, 16, &data[0][0], TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
272 }
273
274 static rtexture_t *draw_generateditherpattern(void)
275 {
276 #if 1
277         int x, y;
278         unsigned char data[8*8*4];
279         for (y = 0;y < 8;y++)
280         {
281                 for (x = 0;x < 8;x++)
282                 {
283                         data[(y*8+x)*4+0] = data[(y*8+x)*4+1] = data[(y*8+x)*4+2] = ((x^y) & 4) ? 255 : 0;
284                         data[(y*8+x)*4+3] = 255;
285                 }
286         }
287         return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, data, TEXTYPE_RGBA, TEXF_FORCENEAREST | TEXF_PRECACHE, NULL);
288 #else
289         unsigned char data[16];
290         memset(data, 255, sizeof(data));
291         data[0] = data[1] = data[2] = data[12] = data[13] = data[14] = 0;
292         return R_LoadTexture2D(drawtexturepool, "ditherpattern", 2, 2, data, TEXTYPE_RGBA, TEXF_FORCENEAREST | TEXF_PRECACHE, NULL);
293 #endif
294 }
295
296 /*
297 ================
298 Draw_CachePic
299 ================
300 */
301 // FIXME: move this to client somehow
302 cachepic_t      *Draw_CachePic (const char *path, qboolean persistent)
303 {
304         int crc, hashkey;
305         cachepic_t *pic;
306         int flags;
307         fs_offset_t lmpsize;
308         unsigned char *lmpdata;
309         char lmpname[MAX_QPATH];
310
311         if (!strncmp(CLVIDEOPREFIX, path, sizeof(CLVIDEOPREFIX) - 1))
312         {
313                 clvideo_t *video;
314
315                 video = CL_GetVideoByName(path);
316                 if( video )
317                         return &video->cpif;
318         }
319
320         crc = CRC_Block((unsigned char *)path, strlen(path));
321         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
322         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
323                 if (!strcmp (path, pic->name))
324                         return pic;
325
326         if (numcachepics == MAX_CACHED_PICS)
327         {
328                 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
329                 // FIXME: support NULL in callers?
330                 return cachepics; // return the first one
331         }
332         pic = cachepics + (numcachepics++);
333         strlcpy (pic->name, path, sizeof(pic->name));
334         // link into list
335         pic->chain = cachepichash[hashkey];
336         cachepichash[hashkey] = pic;
337
338         flags = TEXF_ALPHA;
339         if (persistent)
340                 flags |= TEXF_PRECACHE;
341         if (!strcmp(path, "gfx/colorcontrol/ditherpattern"))
342                 flags |= TEXF_CLAMP;
343
344         // load a high quality image from disk if possible
345         pic->tex = loadtextureimage(drawtexturepool, path, 0, 0, false, flags | (gl_texturecompression_2d.integer ? TEXF_COMPRESS : 0));
346         if (pic->tex == NULL && !strncmp(path, "gfx/", 4))
347         {
348                 // compatibility with older versions which did not require gfx/ prefix
349                 pic->tex = loadtextureimage(drawtexturepool, path + 4, 0, 0, false, flags | (gl_texturecompression_2d.integer ? TEXF_COMPRESS : 0));
350         }
351         // if a high quality image was loaded, set the pic's size to match it, just
352         // in case there's no low quality version to get the size from
353         if (pic->tex)
354         {
355                 pic->width = R_TextureWidth(pic->tex);
356                 pic->height = R_TextureHeight(pic->tex);
357         }
358
359         // now read the low quality version (wad or lmp file), and take the pic
360         // size from that even if we don't upload the texture, this way the pics
361         // show up the right size in the menu even if they were replaced with
362         // higher or lower resolution versions
363         dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
364         if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
365         {
366                 if (lmpsize >= 9)
367                 {
368                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
369                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
370                         // if no high quality replacement image was found, upload the original low quality texture
371                         if (!pic->tex)
372                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags, palette_transparent);
373                 }
374                 Mem_Free(lmpdata);
375         }
376         else if ((lmpdata = W_GetLumpName (path + 4)))
377         {
378                 if (!strcmp(path, "gfx/conchars"))
379                 {
380                         // conchars is a raw image and with color 0 as transparent instead of 255
381                         pic->width = 128;
382                         pic->height = 128;
383                         // if no high quality replacement image was found, upload the original low quality texture
384                         if (!pic->tex)
385                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, flags, palette_font);
386                 }
387                 else
388                 {
389                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
390                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
391                         // if no high quality replacement image was found, upload the original low quality texture
392                         if (!pic->tex)
393                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags, palette_transparent);
394                 }
395         }
396
397         // if it's not found on disk, check if it's one of the builtin images
398         if (pic->tex == NULL)
399         {
400                 if (pic->tex == NULL && !strcmp(path, "gfx/conchars"))
401                         pic->tex = draw_generateconchars();
402                 if (pic->tex == NULL && !strcmp(path, "ui/mousepointer"))
403                         pic->tex = draw_generatemousepointer();
404                 if (pic->tex == NULL && !strcmp(path, "gfx/prydoncursor001"))
405                         pic->tex = draw_generatemousepointer();
406                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair1"))
407                         pic->tex = draw_generatecrosshair(0);
408                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair2"))
409                         pic->tex = draw_generatecrosshair(1);
410                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair3"))
411                         pic->tex = draw_generatecrosshair(2);
412                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair4"))
413                         pic->tex = draw_generatecrosshair(3);
414                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair5"))
415                         pic->tex = draw_generatecrosshair(4);
416                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair6"))
417                         pic->tex = draw_generatecrosshair(5);
418                 if (pic->tex == NULL && !strcmp(path, "gfx/colorcontrol/ditherpattern"))
419                         pic->tex = draw_generateditherpattern();
420                 if (pic->tex == NULL)
421                 {
422                         // don't complain about missing gfx/crosshair images
423                         if (strncmp(path, "gfx/crosshair", 13))
424                                 Con_Printf("Draw_CachePic: failed to load %s\n", path);
425                         pic->tex = r_texture_notexture;
426                 }
427                 pic->width = R_TextureWidth(pic->tex);
428                 pic->height = R_TextureHeight(pic->tex);
429         }
430
431         return pic;
432 }
433
434 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels)
435 {
436         int crc, hashkey;
437         cachepic_t *pic;
438
439         crc = CRC_Block((unsigned char *)picname, strlen(picname));
440         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
441         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
442                 if (!strcmp (picname, pic->name))
443                         break;
444
445         if (pic)
446         {
447                 if (pic->tex && pic->width == width && pic->height == height)
448                 {
449                         R_UpdateTexture(pic->tex, pixels, 0, 0, width, height);
450                         return pic;
451                 }
452         }
453         else
454         {
455                 if (pic == NULL)
456                 {
457                         if (numcachepics == MAX_CACHED_PICS)
458                         {
459                                 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
460                                 // FIXME: support NULL in callers?
461                                 return cachepics; // return the first one
462                         }
463                         pic = cachepics + (numcachepics++);
464                         strlcpy (pic->name, picname, sizeof(pic->name));
465                         // link into list
466                         pic->chain = cachepichash[hashkey];
467                         cachepichash[hashkey] = pic;
468                 }
469         }
470
471         pic->width = width;
472         pic->height = height;
473         if (pic->tex)
474                 R_FreeTexture(pic->tex);
475         pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels, TEXTYPE_RGBA, alpha ? TEXF_ALPHA : 0, NULL);
476         return pic;
477 }
478
479 void Draw_FreePic(const char *picname)
480 {
481         int crc;
482         int hashkey;
483         cachepic_t *pic;
484         // this doesn't really free the pic, but does free it's texture
485         crc = CRC_Block((unsigned char *)picname, strlen(picname));
486         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
487         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
488         {
489                 if (!strcmp (picname, pic->name) && pic->tex)
490                 {
491                         R_FreeTexture(pic->tex);
492                         pic->width = 0;
493                         pic->height = 0;
494                         return;
495                 }
496         }
497 }
498
499 /*
500 ===============
501 Draw_Init
502 ===============
503 */
504 static void gl_draw_start(void)
505 {
506         int i;
507         drawtexturepool = R_AllocTexturePool();
508
509         numcachepics = 0;
510         memset(cachepichash, 0, sizeof(cachepichash));
511
512         char_texture = Draw_CachePic("gfx/conchars", true)->tex;
513         for (i = 1;i <= NUMCROSSHAIRS;i++)
514                 r_crosshairs[i] = Draw_CachePic(va("gfx/crosshair%i", i), true);
515
516         // draw the loading screen so people have something to see in the newly opened window
517         SCR_UpdateLoadingScreen(true);
518 }
519
520 static void gl_draw_shutdown(void)
521 {
522         R_FreeTexturePool(&drawtexturepool);
523
524         numcachepics = 0;
525         memset(cachepichash, 0, sizeof(cachepichash));
526 }
527
528 static void gl_draw_newmap(void)
529 {
530 }
531
532 void GL_Draw_Init (void)
533 {
534         Cvar_RegisterVariable(&r_textshadow);
535         Cvar_RegisterVariable(&r_textbrightness);
536         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
537 }
538
539 static void _DrawQ_Setup(void)
540 {
541         if (r_refdef.draw2dstage)
542                 return;
543         r_refdef.draw2dstage = true;
544         CHECKGLERROR
545         qglViewport(r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
546         GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
547         GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
548         qglDepthFunc(GL_LEQUAL);CHECKGLERROR
549         qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
550         GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
551         R_Mesh_Matrix(&identitymatrix);
552
553         GL_DepthMask(true);
554         GL_DepthRange(0, 1);
555         GL_PolygonOffset(0, 0);
556         GL_DepthTest(false);
557         GL_Color(1,1,1,1);
558         GL_AlphaTest(false);
559         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
560
561         if (gl_support_fragment_shader)
562         {
563                 qglUseProgramObjectARB(0);CHECKGLERROR
564         }
565 }
566
567 static void _DrawQ_ProcessDrawFlag(int flags)
568 {
569         _DrawQ_Setup();
570         CHECKGLERROR
571         if(flags == DRAWFLAG_ADDITIVE)
572                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
573         else if(flags == DRAWFLAG_MODULATE)
574                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
575         else if(flags == DRAWFLAG_2XMODULATE)
576                 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
577         else
578                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
579 }
580
581 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
582 {
583         float floats[20];
584
585         _DrawQ_ProcessDrawFlag(flags);
586         GL_Color(red, green, blue, alpha);
587
588         R_Mesh_VertexPointer(floats, 0, 0);
589         R_Mesh_ColorPointer(NULL, 0, 0);
590         R_Mesh_ResetTextureState();
591         if (pic)
592         {
593                 if (width == 0)
594                         width = pic->width;
595                 if (height == 0)
596                         height = pic->height;
597                 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
598                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
599                 floats[12] = 0;floats[13] = 0;
600                 floats[14] = 1;floats[15] = 0;
601                 floats[16] = 1;floats[17] = 1;
602                 floats[18] = 0;floats[19] = 1;
603         }
604
605         floats[2] = floats[5] = floats[8] = floats[11] = 0;
606         floats[0] = floats[9] = x;
607         floats[1] = floats[4] = y;
608         floats[3] = floats[6] = x + width;
609         floats[7] = floats[10] = y + height;
610
611         R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
612 }
613
614 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
615 {
616         float floats[12];
617
618         _DrawQ_ProcessDrawFlag(flags);
619         GL_Color(red, green, blue, alpha);
620
621         R_Mesh_VertexPointer(floats, 0, 0);
622         R_Mesh_ColorPointer(NULL, 0, 0);
623         R_Mesh_ResetTextureState();
624
625         floats[2] = floats[5] = floats[8] = floats[11] = 0;
626         floats[0] = floats[9] = x;
627         floats[1] = floats[4] = y;
628         floats[3] = floats[6] = x + width;
629         floats[7] = floats[10] = y + height;
630
631         R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
632 }
633
634 // color tag printing
635 static vec4_t string_colors[] =
636 {
637         // Quake3 colors
638         // LordHavoc: why on earth is cyan before magenta in Quake3?
639         // LordHavoc: note: Doom3 uses white for [0] and [7]
640         {0.0, 0.0, 0.0, 1.0}, // black
641         {1.0, 0.0, 0.0, 1.0}, // red
642         {0.0, 1.0, 0.0, 1.0}, // green
643         {1.0, 1.0, 0.0, 1.0}, // yellow
644         {0.0, 0.0, 1.0, 1.0}, // blue
645         {0.0, 1.0, 1.0, 1.0}, // cyan
646         {1.0, 0.0, 1.0, 1.0}, // magenta
647         {1.0, 1.0, 1.0, 1.0}, // white
648         // [515]'s BX_COLOREDTEXT extension
649         {1.0, 1.0, 1.0, 0.5}, // half transparent
650         {0.5, 0.5, 0.5, 1.0}  // half brightness
651         // Black's color table
652         //{1.0, 1.0, 1.0, 1.0},
653         //{1.0, 0.0, 0.0, 1.0},
654         //{0.0, 1.0, 0.0, 1.0},
655         //{0.0, 0.0, 1.0, 1.0},
656         //{1.0, 1.0, 0.0, 1.0},
657         //{0.0, 1.0, 1.0, 1.0},
658         //{1.0, 0.0, 1.0, 1.0},
659         //{0.1, 0.1, 0.1, 1.0}
660 };
661
662 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
663
664 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
665 {
666         float v = r_textbrightness.value;
667         Vector4Copy(string_colors[colorindex], color);
668         Vector4Set(color, (color[0] * (1-v) + v) * r, (color[1] * (1-v) + v) * g, (color[2] * (1-v) + v) * b, color[3] * a);
669         if (shadow)
670         {
671                 float shadowalpha = color[0]+color[1]+color[2] * 0.8;
672                 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
673         }
674 }
675
676 float DrawQ_String(float startx, float starty, const char *text, int maxlen, float w, float h, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor, qboolean ignorecolorcodes)
677 {
678         int i, num, shadow, colorindex = STRING_COLOR_DEFAULT;
679         float x = startx, y, s, t, u, v;
680         float *av, *at, *ac;
681         float color[4];
682         int batchcount;
683         float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
684         float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
685         float color4f[QUADELEMENTS_MAXQUADS*4*4];
686
687         if (maxlen < 1)
688                 maxlen = 1<<30;
689
690         _DrawQ_ProcessDrawFlag(flags);
691
692         R_Mesh_ColorPointer(color4f, 0, 0);
693         R_Mesh_ResetTextureState();
694         R_Mesh_TexBind(0, R_GetTexture(char_texture));
695         R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
696         R_Mesh_VertexPointer(vertex3f, 0, 0);
697
698         ac = color4f;
699         at = texcoord2f;
700         av = vertex3f;
701         batchcount = 0;
702
703         for (shadow = r_textshadow.value != 0;shadow >= 0;shadow--)
704         {
705                 if (!outcolor || *outcolor == -1)
706                         colorindex = STRING_COLOR_DEFAULT;
707                 else
708                         colorindex = *outcolor;
709                 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
710
711                 x = startx;
712                 y = starty;
713                 if (shadow)
714                 {
715                         x += r_textshadow.value;
716                         y += r_textshadow.value;
717                 }
718                 for (i = 0;i < maxlen && text[i];i++, x += w)
719                 {
720                         if (text[i] == ' ')
721                                 continue;
722                         if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen)
723                         {
724                                 if (text[i+1] == STRING_COLOR_TAG)
725                                 {
726                                         i++;
727                                         if (text[i] == ' ')
728                                                 continue;
729                                 }
730                                 else if (text[i+1] >= '0' && text[i+1] <= '9')
731                                 {
732                                         colorindex = text[i+1] - '0';
733                                         DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
734                                         i++;
735                                         x -= w;
736                                         continue;
737                                 }
738                         }
739                         num = text[i];
740                         s = (num & 15)*0.0625f + (0.5f / 256.0f);
741                         t = (num >> 4)*0.0625f + (0.5f / 256.0f);
742                         u = 0.0625f - (1.0f / 256.0f);
743                         v = 0.0625f - (1.0f / 256.0f);
744                         ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
745                         ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
746                         ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
747                         ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
748                         at[ 0] = s  ;at[ 1] = t  ;
749                         at[ 2] = s+u;at[ 3] = t  ;
750                         at[ 4] = s+u;at[ 5] = t+v;
751                         at[ 6] = s  ;at[ 7] = t+v;
752                         av[ 0] = x  ;av[ 1] = y  ;av[ 2] = 10;
753                         av[ 3] = x+w;av[ 4] = y  ;av[ 5] = 10;
754                         av[ 6] = x+w;av[ 7] = y+h;av[ 8] = 10;
755                         av[ 9] = x  ;av[10] = y+h;av[11] = 10;
756                         ac += 16;
757                         at += 8;
758                         av += 12;
759                         batchcount++;
760                         if (batchcount >= QUADELEMENTS_MAXQUADS)
761                         {
762                                 if (basealpha >= (1.0f / 255.0f))
763                                 {
764                                         GL_LockArrays(0, batchcount * 4);
765                                         R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
766                                         GL_LockArrays(0, 0);
767                                 }
768                                 batchcount = 0;
769                                 ac = color4f;
770                                 at = texcoord2f;
771                                 av = vertex3f;
772                         }
773                 }
774         }
775         if (batchcount > 0)
776         {
777                 if (basealpha >= (1.0f / 255.0f))
778                 {
779                         GL_LockArrays(0, batchcount * 4);
780                         R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
781                         GL_LockArrays(0, 0);
782                 }
783         }
784
785         if (outcolor)
786                 *outcolor = colorindex;
787
788         // note: this relies on the proper text (not shadow) being drawn last
789         return x;
790 }
791
792 #if 0
793 // not used
794 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
795 {
796         int color, numchars = 0;
797         char *outputend2c = output2c + maxoutchars - 2;
798         if (!outcolor || *outcolor == -1)
799                 color = STRING_COLOR_DEFAULT;
800         else
801                 color = *outcolor;
802         if (!maxreadchars)
803                 maxreadchars = 1<<30;
804         textend = text + maxreadchars;
805         while (text != textend && *text)
806         {
807                 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
808                 {
809                         if (text[1] == STRING_COLOR_TAG)
810                                 text++;
811                         else if (text[1] >= '0' && text[1] <= '9')
812                         {
813                                 color = text[1] - '0';
814                                 text += 2;
815                                 continue;
816                         }
817                 }
818                 if (output2c >= outputend2c)
819                         break;
820                 *output2c++ = *text++;
821                 *output2c++ = color;
822                 numchars++;
823         }
824         output2c[0] = output2c[1] = 0;
825         if (outcolor)
826                 *outcolor = color;
827         return numchars;
828 }
829 #endif
830
831 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)
832 {
833         float floats[36];
834
835         _DrawQ_ProcessDrawFlag(flags);
836
837         R_Mesh_VertexPointer(floats, 0, 0);
838         R_Mesh_ColorPointer(floats + 20, 0, 0);
839         R_Mesh_ResetTextureState();
840         if (pic)
841         {
842                 if (width == 0)
843                         width = pic->width;
844                 if (height == 0)
845                         height = pic->height;
846                 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
847                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
848                 floats[12] = s1;floats[13] = t1;
849                 floats[14] = s2;floats[15] = t2;
850                 floats[16] = s4;floats[17] = t4;
851                 floats[18] = s3;floats[19] = t3;
852         }
853
854         floats[2] = floats[5] = floats[8] = floats[11] = 0;
855         floats[0] = floats[9] = x;
856         floats[1] = floats[4] = y;
857         floats[3] = floats[6] = x + width;
858         floats[7] = floats[10] = y + height;
859         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
860         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
861         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
862         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
863
864         R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
865 }
866
867 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
868 {
869         _DrawQ_ProcessDrawFlag(flags);
870
871         R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
872         R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
873         R_Mesh_ResetTextureState();
874         R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
875         R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
876
877         GL_LockArrays(0, mesh->num_vertices);
878         R_Mesh_Draw(0, mesh->num_vertices, mesh->num_triangles, mesh->data_element3i, 0, 0);
879         GL_LockArrays(0, 0);
880 }
881
882 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
883 {
884         int num;
885
886         _DrawQ_ProcessDrawFlag(flags);
887
888         GL_Color(1,1,1,1);
889         CHECKGLERROR
890         qglBegin(GL_LINE_LOOP);
891         for (num = 0;num < mesh->num_vertices;num++)
892         {
893                 if (mesh->data_color4f)
894                         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]);
895                 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
896         }
897         qglEnd();
898         CHECKGLERROR
899 }
900
901 //[515]: this is old, delete
902 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
903 {
904         _DrawQ_ProcessDrawFlag(flags);
905
906         CHECKGLERROR
907         qglLineWidth(width);CHECKGLERROR
908
909         GL_Color(r,g,b,alpha);
910         CHECKGLERROR
911         qglBegin(GL_LINES);
912         qglVertex2f(x1, y1);
913         qglVertex2f(x2, y2);
914         qglEnd();
915         CHECKGLERROR
916 }
917
918 void DrawQ_SetClipArea(float x, float y, float width, float height)
919 {
920         _DrawQ_Setup();
921
922         // We have to convert the con coords into real coords
923         // OGL uses top to bottom
924         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)));
925
926         GL_ScissorTest(true);
927 }
928
929 void DrawQ_ResetClipArea(void)
930 {
931         _DrawQ_Setup();
932         GL_ScissorTest(false);
933 }
934
935 void DrawQ_Finish(void)
936 {
937         r_refdef.draw2dstage = false;
938 }
939
940 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
941 void R_DrawGamma(void)
942 {
943         float c[4];
944         if (!vid_usinghwgamma)
945         {
946                 // all the blends ignore depth
947                 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
948                 R_Mesh_ColorPointer(NULL, 0, 0);
949                 R_Mesh_ResetTextureState();
950                 GL_DepthMask(true);
951                 GL_DepthRange(0, 1);
952                 GL_PolygonOffset(0, 0);
953                 GL_DepthTest(false);
954                 if (v_color_enable.integer)
955                 {
956                         c[0] = v_color_white_r.value;
957                         c[1] = v_color_white_g.value;
958                         c[2] = v_color_white_b.value;
959                 }
960                 else
961                         c[0] = c[1] = c[2] = v_contrast.value;
962                 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
963                 {
964                         GL_BlendFunc(GL_DST_COLOR, GL_ONE);
965                         while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
966                         {
967                                 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
968                                 R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);
969                                 VectorScale(c, 0.5, c);
970                         }
971                 }
972                 if (v_color_enable.integer)
973                 {
974                         c[0] = v_color_black_r.value;
975                         c[1] = v_color_black_g.value;
976                         c[2] = v_color_black_b.value;
977                 }
978                 else
979                         c[0] = c[1] = c[2] = v_brightness.value;
980                 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
981                 {
982                         GL_BlendFunc(GL_ONE, GL_ONE);
983                         GL_Color(c[0], c[1], c[2], 1);
984                         R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);
985                 }
986         }
987 }
988