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