]> icculus.org git repositories - divverent/darkplaces.git/blob - gl_draw.c
changed description for gl_finish cvar according to esteel's suggestion
[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 = {0, "r_textshadow", "0", "draws a shadow on all text to improve readability"};
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), 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
343         // load a high quality image from disk if possible
344         pic->tex = loadtextureimage(drawtexturepool, path, 0, 0, false, flags);
345         if (pic->tex == NULL && !strncmp(path, "gfx/", 4))
346         {
347                 // compatibility with older versions which did not require gfx/ prefix
348                 pic->tex = loadtextureimage(drawtexturepool, path + 4, 0, 0, false, flags);
349         }
350         // if a high quality image was loaded, set the pic's size to match it, just
351         // in case there's no low quality version to get the size from
352         if (pic->tex)
353         {
354                 pic->width = R_TextureWidth(pic->tex);
355                 pic->height = R_TextureHeight(pic->tex);
356         }
357
358         // now read the low quality version (wad or lmp file), and take the pic
359         // size from that even if we don't upload the texture, this way the pics
360         // show up the right size in the menu even if they were replaced with
361         // higher or lower resolution versions
362         dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
363         if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
364         {
365                 if (lmpsize >= 9)
366                 {
367                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
368                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
369                         // if no high quality replacement image was found, upload the original low quality texture
370                         if (!pic->tex)
371                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags, palette_transparent);
372                 }
373                 Mem_Free(lmpdata);
374         }
375         else if ((lmpdata = W_GetLumpName (path + 4)))
376         {
377                 if (!strcmp(path, "gfx/conchars"))
378                 {
379                         // conchars is a raw image and with color 0 as transparent instead of 255
380                         pic->width = 128;
381                         pic->height = 128;
382                         // if no high quality replacement image was found, upload the original low quality texture
383                         if (!pic->tex)
384                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, flags, palette_font);
385                 }
386                 else
387                 {
388                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
389                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
390                         // if no high quality replacement image was found, upload the original low quality texture
391                         if (!pic->tex)
392                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags, palette_transparent);
393                 }
394         }
395
396         // if it's not found on disk, check if it's one of the builtin images
397         if (pic->tex == NULL)
398         {
399                 if (pic->tex == NULL && !strcmp(path, "gfx/conchars"))
400                         pic->tex = draw_generateconchars();
401                 if (pic->tex == NULL && !strcmp(path, "ui/mousepointer"))
402                         pic->tex = draw_generatemousepointer();
403                 if (pic->tex == NULL && !strcmp(path, "gfx/prydoncursor001"))
404                         pic->tex = draw_generatemousepointer();
405                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair1"))
406                         pic->tex = draw_generatecrosshair(0);
407                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair2"))
408                         pic->tex = draw_generatecrosshair(1);
409                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair3"))
410                         pic->tex = draw_generatecrosshair(2);
411                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair4"))
412                         pic->tex = draw_generatecrosshair(3);
413                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair5"))
414                         pic->tex = draw_generatecrosshair(4);
415                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair6"))
416                         pic->tex = draw_generatecrosshair(5);
417                 if (pic->tex == NULL && !strcmp(path, "gfx/colorcontrol/ditherpattern"))
418                         pic->tex = draw_generateditherpattern();
419                 if (pic->tex == NULL)
420                 {
421                         Con_Printf("Draw_CachePic: failed to load %s\n", path);
422                         pic->tex = r_texture_notexture;
423                 }
424                 pic->width = R_TextureWidth(pic->tex);
425                 pic->height = R_TextureHeight(pic->tex);
426         }
427
428         return pic;
429 }
430
431 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels)
432 {
433         int crc, hashkey;
434         cachepic_t *pic;
435
436         crc = CRC_Block((unsigned char *)picname, strlen(picname));
437         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
438         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
439                 if (!strcmp (picname, pic->name))
440                         break;
441
442         if (pic)
443         {
444                 if (pic->tex && pic->width == width && pic->height == height)
445                 {
446                         R_UpdateTexture(pic->tex, pixels, 0, 0, width, height);
447                         return pic;
448                 }
449         }
450         else
451         {
452                 if (pic == NULL)
453                 {
454                         if (numcachepics == MAX_CACHED_PICS)
455                         {
456                                 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
457                                 // FIXME: support NULL in callers?
458                                 return cachepics; // return the first one
459                         }
460                         pic = cachepics + (numcachepics++);
461                         strlcpy (pic->name, picname, sizeof(pic->name));
462                         // link into list
463                         pic->chain = cachepichash[hashkey];
464                         cachepichash[hashkey] = pic;
465                 }
466         }
467
468         pic->width = width;
469         pic->height = height;
470         if (pic->tex)
471                 R_FreeTexture(pic->tex);
472         pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels, TEXTYPE_RGBA, alpha ? TEXF_ALPHA : 0, NULL);
473         return pic;
474 }
475
476 void Draw_FreePic(const char *picname)
477 {
478         int crc;
479         int hashkey;
480         cachepic_t *pic;
481         // this doesn't really free the pic, but does free it's texture
482         crc = CRC_Block((unsigned char *)picname, strlen(picname));
483         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
484         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
485         {
486                 if (!strcmp (picname, pic->name) && pic->tex)
487                 {
488                         R_FreeTexture(pic->tex);
489                         pic->width = 0;
490                         pic->height = 0;
491                         return;
492                 }
493         }
494 }
495
496 /*
497 ===============
498 Draw_Init
499 ===============
500 */
501 static void gl_draw_start(void)
502 {
503         int i;
504         drawtexturepool = R_AllocTexturePool();
505
506         numcachepics = 0;
507         memset(cachepichash, 0, sizeof(cachepichash));
508
509         char_texture = Draw_CachePic("gfx/conchars", true)->tex;
510         for (i = 1;i <= NUMCROSSHAIRS;i++)
511                 r_crosshairs[i] = Draw_CachePic(va("gfx/crosshair%i", i), true);
512 }
513
514 static void gl_draw_shutdown(void)
515 {
516         R_FreeTexturePool(&drawtexturepool);
517
518         numcachepics = 0;
519         memset(cachepichash, 0, sizeof(cachepichash));
520 }
521
522 static void gl_draw_newmap(void)
523 {
524 }
525
526 void GL_Draw_Init (void)
527 {
528         Cvar_RegisterVariable(&r_textshadow);
529         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
530 }
531
532 static void _DrawQ_Setup(void)
533 {
534         if (r_refdef.draw2dstage)
535                 return;
536         r_refdef.draw2dstage = true;
537         CHECKGLERROR
538         qglViewport(r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
539         GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
540         GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
541         qglDepthFunc(GL_LEQUAL);CHECKGLERROR
542         qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
543         R_Mesh_Matrix(&identitymatrix);
544
545         GL_DepthMask(true);
546         GL_DepthTest(false);
547         GL_Color(1,1,1,1);
548         GL_AlphaTest(false);
549         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
550
551         if (gl_support_fragment_shader)
552         {
553                 qglUseProgramObjectARB(0);CHECKGLERROR
554         }
555 }
556
557 static void _DrawQ_ProcessDrawFlag(int flags)
558 {
559         _DrawQ_Setup();
560         CHECKGLERROR
561         if(flags == DRAWFLAG_ADDITIVE)
562                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
563         else if(flags == DRAWFLAG_MODULATE)
564                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
565         else if(flags == DRAWFLAG_2XMODULATE)
566                 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
567         else
568                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
569 }
570
571 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
572 {
573         DrawQ_SuperPic(x,y,pic,width,height,0,0,red,green,blue,alpha,1,0,red,green,blue,alpha,0,1,red,green,blue,alpha,1,1,red,green,blue,alpha,flags);
574 }
575
576 void DrawQ_String_Real(float x, float y, const char *string, int maxlen, float w, float h, float red, float green, float blue, float alpha, int flags)
577 {
578         int i, num;
579         float *av, *at;
580         int batchcount;
581         float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
582         float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
583
584         if (alpha < (1.0f / 255.0f))
585                 return;
586
587         _DrawQ_ProcessDrawFlag(flags);
588
589         GL_Color(red, green, blue, alpha);
590
591         R_Mesh_VertexPointer(vertex3f);
592         R_Mesh_ColorPointer(NULL);
593         R_Mesh_ResetTextureState();
594         R_Mesh_TexBind(0, R_GetTexture(char_texture));
595         R_Mesh_TexCoordPointer(0, 2, texcoord2f);
596
597         at = texcoord2f;
598         av = vertex3f;
599         batchcount = 0;
600
601         if (maxlen < 1)
602                 maxlen = 9999;
603         for (i = 0;i < maxlen && x < vid_conwidth.integer && (num = string[i]);i++, x += w)
604         {
605                 float s, t, u, v;
606                 if (num == ' ')
607                         continue;
608                 s = (num & 15)*0.0625f + (0.5f / 256.0f);
609                 t = (num >> 4)*0.0625f + (0.5f / 256.0f);
610                 u = 0.0625f - (1.0f / 256.0f);
611                 v = 0.0625f - (1.0f / 256.0f);
612                 at[ 0] = s  ;at[ 1] = t  ;
613                 at[ 2] = s+u;at[ 3] = t  ;
614                 at[ 4] = s+u;at[ 5] = t+v;
615                 at[ 6] = s  ;at[ 7] = t+v;
616                 av[ 0] = x  ;av[ 1] = y  ;av[ 2] = 10;
617                 av[ 3] = x+w;av[ 4] = y  ;av[ 5] = 10;
618                 av[ 6] = x+w;av[ 7] = y+h;av[ 8] = 10;
619                 av[ 9] = x  ;av[10] = y+h;av[11] = 10;
620                 at += 8;
621                 av += 12;
622                 batchcount++;
623                 if (batchcount >= QUADELEMENTS_MAXQUADS)
624                 {
625                         GL_LockArrays(0, batchcount * 4);
626                         R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements);
627                         GL_LockArrays(0, 0);
628                         batchcount = 0;
629                         at = texcoord2f;
630                         av = vertex3f;
631                 }
632         }
633         if (batchcount > 0)
634         {
635                 GL_LockArrays(0, batchcount * 4);
636                 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements);
637                 GL_LockArrays(0, 0);
638         }
639 }
640
641 void DrawQ_String(float x, float y, const char *string, int maxlen, float scalex, float scaley, float red, float green, float blue, float alpha, int flags)
642 {
643         if (r_textshadow.integer)
644                 DrawQ_String_Real(x+scalex*0.25,y+scaley*0.25,string,maxlen,scalex,scaley,0,0,0,alpha*0.8,flags);
645
646         DrawQ_String_Real(x,y,string,maxlen,scalex,scaley,red,green,blue,alpha,flags);
647 }
648
649 // color tag printing
650 static vec4_t string_colors[] =
651 {
652         // Quake3 colors
653         // LordHavoc: why on earth is cyan before magenta in Quake3?
654         // LordHavoc: note: Doom3 uses white for [0] and [7]
655         {0.0, 0.0, 0.0, 1.0}, // black
656         {1.0, 0.0, 0.0, 1.0}, // red
657         {0.0, 1.0, 0.0, 1.0}, // green
658         {1.0, 1.0, 0.0, 1.0}, // yellow
659         {0.0, 0.0, 1.0, 1.0}, // blue
660         {0.0, 1.0, 1.0, 1.0}, // cyan
661         {1.0, 0.0, 1.0, 1.0}, // magenta
662         {1.0, 1.0, 1.0, 1.0}, // white
663         // [515]'s BX_COLOREDTEXT extension
664         {1.0, 1.0, 1.0, 0.5}, // half transparent
665         {0.5, 0.5, 0.5, 1.0}  // half brightness
666         // Black's color table
667         //{1.0, 1.0, 1.0, 1.0},
668         //{1.0, 0.0, 0.0, 1.0},
669         //{0.0, 1.0, 0.0, 1.0},
670         //{0.0, 0.0, 1.0, 1.0},
671         //{1.0, 1.0, 0.0, 1.0},
672         //{0.0, 1.0, 1.0, 1.0},
673         //{1.0, 0.0, 1.0, 1.0},
674         //{0.1, 0.1, 0.1, 1.0}
675 };
676
677 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
678
679 // color is read and changed in the end
680 void DrawQ_ColoredString( float x, float y, const char *text, int maxlen, float scalex, float scaley, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor )
681 {
682         vec_t *color;
683         int len;
684         int colorindex;
685         const char *start, *current;
686
687         if( !outcolor || *outcolor == -1 ) {
688                 colorindex = STRING_COLOR_DEFAULT;
689         } else {
690                 colorindex = *outcolor;
691         }
692         color = string_colors[colorindex];
693
694         if( maxlen < 1)
695                 len = (int)strlen( text );
696         else
697                 len = min( maxlen, (int) strlen( text ) );
698
699         start = current = text;
700         while( len > 0 ) {
701                 // check for color control char
702                 if( *current == STRING_COLOR_TAG ) {
703                         // get next char
704                         current++;
705                         len--;
706                         if( len == 0 ) {
707                                 break;
708                         }
709                         // display the tag char?
710                         if( *current == STRING_COLOR_TAG ) {
711                                 // only display one of the two
712                                 start = current;
713                                 // get the next char
714                                 current++;
715                                 len--;
716                         } else if( '0' <= *current && *current <= '9' ) {
717                                 colorindex = 0;
718                                 do {
719                                         colorindex = colorindex * 10 + (*current - '0');
720                                         // only read as long as it makes a valid index
721                                         if( colorindex >= (int)STRING_COLORS_COUNT ) {
722                                                 // undo the last operation
723                                                 colorindex /= 10;
724                                                 break;
725                                         }
726                                         current++;
727                                         len--;
728                                 } while( len > 0 && '0' <= *current && *current <= '9' );
729                                 // set the color
730                                 color = string_colors[colorindex];
731                                 // we jump over the color tag
732                                 start = current;
733                         }
734                 }
735                 // go on and read normal text in until the next control char
736                 while( len > 0 && *current != STRING_COLOR_TAG ) {
737                         current++;
738                         len--;
739                 }
740                 // display the text
741                 if( start != current ) {
742                         // draw the string
743                         DrawQ_String( x, y, start, current - start, scalex, scaley, basered * color[0], basegreen * color[1], baseblue * color[2], basealpha * color[3], flags );
744                         // update x to be at the new start position
745                         x += (current - start) * scalex;
746                         // set start accordingly
747                         start = current;
748                 }
749         }
750
751         // return the last colorindex
752         if( outcolor ) {
753                 *outcolor = colorindex;
754         }
755 }
756
757 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)
758 {
759         float floats[36];
760
761         _DrawQ_ProcessDrawFlag(flags);
762
763         R_Mesh_VertexPointer(floats);
764         R_Mesh_ColorPointer(floats + 20);
765         R_Mesh_ResetTextureState();
766         if (pic)
767         {
768                 if (width == 0)
769                         width = pic->width;
770                 if (height == 0)
771                         height = pic->height;
772                 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
773                 R_Mesh_TexCoordPointer(0, 2, floats + 12);
774                 floats[12] = s1;floats[13] = t1;
775                 floats[14] = s2;floats[15] = t2;
776                 floats[16] = s4;floats[17] = t4;
777                 floats[18] = s3;floats[19] = t3;
778         }
779
780         floats[2] = floats[5] = floats[8] = floats[11] = 0;
781         floats[0] = floats[9] = x;
782         floats[1] = floats[4] = y;
783         floats[3] = floats[6] = x + width;
784         floats[7] = floats[10] = y + height;
785         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
786         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
787         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
788         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
789
790         R_Mesh_Draw(0, 4, 2, polygonelements);
791 }
792
793 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
794 {
795         _DrawQ_ProcessDrawFlag(flags);
796
797         R_Mesh_VertexPointer(mesh->data_vertex3f);
798         R_Mesh_ColorPointer(mesh->data_color4f);
799         R_Mesh_ResetTextureState();
800         R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
801         R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f);
802
803         GL_LockArrays(0, mesh->num_vertices);
804         R_Mesh_Draw(0, mesh->num_vertices, mesh->num_triangles, mesh->data_element3i);
805         GL_LockArrays(0, 0);
806 }
807
808 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
809 {
810         int num;
811
812         _DrawQ_ProcessDrawFlag(flags);
813
814         GL_Color(1,1,1,1);
815         CHECKGLERROR
816         qglBegin(GL_LINE_LOOP);
817         for (num = 0;num < mesh->num_vertices;num++)
818         {
819                 if (mesh->data_color4f)
820                         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]);
821                 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
822         }
823         qglEnd();
824         CHECKGLERROR
825 }
826
827 //[515]: this is old, delete
828 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
829 {
830         _DrawQ_ProcessDrawFlag(flags);
831
832         CHECKGLERROR
833         qglLineWidth(width);CHECKGLERROR
834
835         GL_Color(r,g,b,alpha);
836         CHECKGLERROR
837         qglBegin(GL_LINES);
838         qglVertex2f(x1, y1);
839         qglVertex2f(x2, y2);
840         qglEnd();
841         CHECKGLERROR
842 }
843
844 void DrawQ_SetClipArea(float x, float y, float width, float height)
845 {
846         _DrawQ_Setup();
847
848         // We have to convert the con coords into real coords
849         // OGL uses top to bottom
850         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)));
851
852         GL_ScissorTest(true);
853 }
854
855 void DrawQ_ResetClipArea(void)
856 {
857         _DrawQ_Setup();
858         GL_ScissorTest(false);
859 }
860
861 void DrawQ_Finish(void)
862 {
863         r_refdef.draw2dstage = false;
864 }
865
866 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
867 void R_DrawGamma(void)
868 {
869         float c[4];
870         if (!vid_usinghwgamma)
871         {
872                 // all the blends ignore depth
873                 R_Mesh_VertexPointer(blendvertex3f);
874                 R_Mesh_ColorPointer(NULL);
875                 R_Mesh_ResetTextureState();
876                 GL_DepthMask(true);
877                 GL_DepthTest(false);
878                 if (v_color_enable.integer)
879                 {
880                         c[0] = v_color_white_r.value;
881                         c[1] = v_color_white_g.value;
882                         c[2] = v_color_white_b.value;
883                 }
884                 else
885                         c[0] = c[1] = c[2] = v_contrast.value;
886                 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
887                 {
888                         GL_BlendFunc(GL_DST_COLOR, GL_ONE);
889                         while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
890                         {
891                                 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
892                                 R_Mesh_Draw(0, 3, 1, polygonelements);
893                                 VectorScale(c, 0.5, c);
894                         }
895                 }
896                 if (v_color_enable.integer)
897                 {
898                         c[0] = v_color_black_r.value;
899                         c[1] = v_color_black_g.value;
900                         c[2] = v_color_black_b.value;
901                 }
902                 else
903                         c[0] = c[1] = c[2] = v_brightness.value;
904                 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
905                 {
906                         GL_BlendFunc(GL_ONE, GL_ONE);
907                         GL_Color(c[0], c[1], c[2], 1);
908                         R_Mesh_Draw(0, 3, 1, polygonelements);
909                 }
910         }
911 }
912