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