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