remove some r_render checks, and added an r_render check to vid_sdl.c VID_Finish
[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] = 255;
267                         data[i][1] = 255;
268                         data[i][2] = 255;
269                         data[i][3] = (unsigned char) ((int) (in[i] - '0') * 255 / 7);
270                 }
271         }
272         return R_LoadTexture2D(drawtexturepool, va("crosshair%i", num), 16, 16, &data[0][0], TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
273 }
274
275 static rtexture_t *draw_generateditherpattern(void)
276 {
277 #if 1
278         int x, y;
279         unsigned char data[8*8*4];
280         for (y = 0;y < 8;y++)
281         {
282                 for (x = 0;x < 8;x++)
283                 {
284                         data[(y*8+x)*4+0] = data[(y*8+x)*4+1] = data[(y*8+x)*4+2] = ((x^y) & 4) ? 255 : 0;
285                         data[(y*8+x)*4+3] = 255;
286                 }
287         }
288         return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, data, TEXTYPE_RGBA, TEXF_FORCENEAREST | TEXF_PRECACHE, NULL);
289 #else
290         unsigned char data[16];
291         memset(data, 255, sizeof(data));
292         data[0] = data[1] = data[2] = data[12] = data[13] = data[14] = 0;
293         return R_LoadTexture2D(drawtexturepool, "ditherpattern", 2, 2, data, TEXTYPE_RGBA, TEXF_FORCENEAREST | TEXF_PRECACHE, NULL);
294 #endif
295 }
296
297 /*
298 ================
299 Draw_CachePic
300 ================
301 */
302 // FIXME: move this to client somehow
303 cachepic_t      *Draw_CachePic (const char *path, qboolean persistent)
304 {
305         int crc, hashkey;
306         cachepic_t *pic;
307         qpic_t *p;
308         int flags;
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 the pic from disk
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
348                 pic->tex = loadtextureimage(drawtexturepool, path + 4, 0, 0, false, flags);
349                 // failed to find gfx/whatever.tga or similar, try the wad
350                 if (pic->tex == NULL && (p = (qpic_t *)W_GetLumpName (path + 4)))
351                 {
352                         if (!strcmp(path, "gfx/conchars"))
353                         {
354                                 // conchars is a raw image and with color 0 as transparent instead of 255
355                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, (unsigned char *)p, TEXTYPE_PALETTE, flags, palette_font);
356                         }
357                         else
358                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, p->width, p->height, p->data, TEXTYPE_PALETTE, flags, palette_transparent);
359                 }
360         }
361
362         if (pic->tex == NULL && !strcmp(path, "gfx/conchars"))
363                 pic->tex = draw_generateconchars();
364         if (pic->tex == NULL && !strcmp(path, "ui/mousepointer"))
365                 pic->tex = draw_generatemousepointer();
366         if (pic->tex == NULL && !strcmp(path, "gfx/prydoncursor001"))
367                 pic->tex = draw_generatemousepointer();
368         if (pic->tex == NULL && !strcmp(path, "gfx/crosshair1"))
369                 pic->tex = draw_generatecrosshair(0);
370         if (pic->tex == NULL && !strcmp(path, "gfx/crosshair2"))
371                 pic->tex = draw_generatecrosshair(1);
372         if (pic->tex == NULL && !strcmp(path, "gfx/crosshair3"))
373                 pic->tex = draw_generatecrosshair(2);
374         if (pic->tex == NULL && !strcmp(path, "gfx/crosshair4"))
375                 pic->tex = draw_generatecrosshair(3);
376         if (pic->tex == NULL && !strcmp(path, "gfx/crosshair5"))
377                 pic->tex = draw_generatecrosshair(4);
378         if (pic->tex == NULL && !strcmp(path, "gfx/crosshair6"))
379                 pic->tex = draw_generatecrosshair(5);
380         if (pic->tex == NULL && !strcmp(path, "gfx/colorcontrol/ditherpattern"))
381                 pic->tex = draw_generateditherpattern();
382         if (pic->tex == NULL)
383         {
384                 Con_Printf("Draw_CachePic: failed to load %s\n", path);
385                 pic->tex = r_texture_notexture;
386         }
387
388         pic->width = R_TextureWidth(pic->tex);
389         pic->height = R_TextureHeight(pic->tex);
390         return pic;
391 }
392
393 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels)
394 {
395         int crc, hashkey;
396         cachepic_t *pic;
397
398         crc = CRC_Block((unsigned char *)picname, strlen(picname));
399         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
400         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
401                 if (!strcmp (picname, pic->name))
402                         break;
403
404         if (pic)
405         {
406                 if (pic->tex && pic->width == width && pic->height == height)
407                 {
408                         R_UpdateTexture(pic->tex, pixels, 0, 0, width, height);
409                         return pic;
410                 }
411         }
412         else
413         {
414                 if (pic == NULL)
415                 {
416                         if (numcachepics == MAX_CACHED_PICS)
417                         {
418                                 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
419                                 // FIXME: support NULL in callers?
420                                 return cachepics; // return the first one
421                         }
422                         pic = cachepics + (numcachepics++);
423                         strcpy (pic->name, picname);
424                         // link into list
425                         pic->chain = cachepichash[hashkey];
426                         cachepichash[hashkey] = pic;
427                 }
428         }
429
430         pic->width = width;
431         pic->height = height;
432         if (pic->tex)
433                 R_FreeTexture(pic->tex);
434         pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels, TEXTYPE_RGBA, alpha ? TEXF_ALPHA : 0, NULL);
435         return pic;
436 }
437
438 void Draw_FreePic(const char *picname)
439 {
440         int crc;
441         int hashkey;
442         cachepic_t *pic;
443         // this doesn't really free the pic, but does free it's texture
444         crc = CRC_Block((unsigned char *)picname, strlen(picname));
445         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
446         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
447         {
448                 if (!strcmp (picname, pic->name) && pic->tex)
449                 {
450                         R_FreeTexture(pic->tex);
451                         pic->width = 0;
452                         pic->height = 0;
453                         return;
454                 }
455         }
456 }
457
458 /*
459 ===============
460 Draw_Init
461 ===============
462 */
463 static void gl_draw_start(void)
464 {
465         int i;
466         drawtexturepool = R_AllocTexturePool();
467
468         numcachepics = 0;
469         memset(cachepichash, 0, sizeof(cachepichash));
470
471         char_texture = Draw_CachePic("gfx/conchars", true)->tex;
472         for (i = 1;i <= NUMCROSSHAIRS;i++)
473                 r_crosshairs[i] = Draw_CachePic(va("gfx/crosshair%i", i), true);
474 }
475
476 static void gl_draw_shutdown(void)
477 {
478         R_FreeTexturePool(&drawtexturepool);
479
480         numcachepics = 0;
481         memset(cachepichash, 0, sizeof(cachepichash));
482 }
483
484 static void gl_draw_newmap(void)
485 {
486 }
487
488 void GL_Draw_Init (void)
489 {
490         Cvar_RegisterVariable(&r_textshadow);
491         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
492 }
493
494 void DrawQ_Begin(void)
495 {
496         GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
497
498         CHECKGLERROR
499         qglViewport(r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
500         GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
501         qglDepthFunc(GL_LEQUAL);CHECKGLERROR
502         R_Mesh_Matrix(&identitymatrix);
503
504         GL_DepthMask(true);
505         GL_DepthTest(false);
506         GL_Color(1,1,1,1);
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