38c6536585b3c00d716f06686425cad2df070cf8
[xonotic/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         r_view_width = bound(0, r_refdef.width, vid.width);
497         r_view_height = bound(0, r_refdef.height, vid.height);
498         r_view_depth = 1;
499         r_view_x = bound(0, r_refdef.x, vid.width - r_refdef.width);
500         r_view_y = bound(0, r_refdef.y, vid.height - r_refdef.height);
501         r_view_z = 0;
502         r_view_matrix = r_refdef.viewentitymatrix;
503         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
504
505         CHECKGLERROR
506         qglViewport(r_view_x, vid.height - (r_view_y + r_view_height), r_view_width, r_view_height);CHECKGLERROR
507         GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
508         qglDepthFunc(GL_LEQUAL);CHECKGLERROR
509         R_Mesh_Matrix(&identitymatrix);
510
511         GL_DepthMask(true);
512         GL_DepthTest(false);
513         GL_Color(1,1,1,1);
514
515         r_refdef.draw2dstage = true;
516 }
517
518 static void _DrawQ_ProcessDrawFlag(int flags)
519 {
520         CHECKGLERROR
521         if(flags == DRAWFLAG_ADDITIVE)
522                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
523         else if(flags == DRAWFLAG_MODULATE)
524                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
525         else if(flags == DRAWFLAG_2XMODULATE)
526                 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
527         else
528                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
529 }
530
531 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
532 {
533         if (!r_refdef.draw2dstage)
534         {
535                 Con_Printf("DrawQ_Pic: not in 2d rendering stage!\n");
536                 return;
537         }
538         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);
539 }
540
541 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)
542 {
543         int i, num;
544         float *av, *at;
545         int batchcount;
546         float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
547         float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
548
549         if (!r_refdef.draw2dstage)
550         {
551                 Con_Printf("DrawQ_String: not in 2d rendering stage!\n");
552                 return;
553         }
554
555         if (!r_render.integer)
556                 return;
557
558         if (alpha < (1.0f / 255.0f))
559                 return;
560
561         _DrawQ_ProcessDrawFlag(flags);
562
563         GL_Color(red, green, blue, alpha);
564
565         R_Mesh_VertexPointer(vertex3f);
566         R_Mesh_ColorPointer(NULL);
567         R_Mesh_ResetTextureState();
568         R_Mesh_TexBind(0, R_GetTexture(char_texture));
569         R_Mesh_TexCoordPointer(0, 2, texcoord2f);
570
571         at = texcoord2f;
572         av = vertex3f;
573         batchcount = 0;
574
575         if (maxlen < 1)
576                 maxlen = 9999;
577         for (i = 0;i < maxlen && x < vid_conwidth.integer && (num = string[i]);i++, x += w)
578         {
579                 float s, t, u, v;
580                 if (num == ' ')
581                         continue;
582                 s = (num & 15)*0.0625f + (0.5f / 256.0f);
583                 t = (num >> 4)*0.0625f + (0.5f / 256.0f);
584                 u = 0.0625f - (1.0f / 256.0f);
585                 v = 0.0625f - (1.0f / 256.0f);
586                 at[ 0] = s  ;at[ 1] = t  ;
587                 at[ 2] = s+u;at[ 3] = t  ;
588                 at[ 4] = s+u;at[ 5] = t+v;
589                 at[ 6] = s  ;at[ 7] = t+v;
590                 av[ 0] = x  ;av[ 1] = y  ;av[ 2] = 10;
591                 av[ 3] = x+w;av[ 4] = y  ;av[ 5] = 10;
592                 av[ 6] = x+w;av[ 7] = y+h;av[ 8] = 10;
593                 av[ 9] = x  ;av[10] = y+h;av[11] = 10;
594                 at += 8;
595                 av += 12;
596                 batchcount++;
597                 if (batchcount >= QUADELEMENTS_MAXQUADS)
598                 {
599                         GL_LockArrays(0, batchcount * 4);
600                         R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements);
601                         GL_LockArrays(0, 0);
602                         batchcount = 0;
603                         at = texcoord2f;
604                         av = vertex3f;
605                 }
606         }
607         if (batchcount > 0)
608         {
609                 GL_LockArrays(0, batchcount * 4);
610                 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements);
611                 GL_LockArrays(0, 0);
612         }
613 }
614
615 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)
616 {
617         if (!r_refdef.draw2dstage)
618         {
619                 Con_Printf("DrawQ_String: not in 2d rendering stage!\n");
620                 return;
621         }
622
623         if (r_textshadow.integer)
624                 DrawQ_String_Real(x+scalex*0.25,y+scaley*0.25,string,maxlen,scalex,scaley,0,0,0,alpha*0.8,flags);
625
626         DrawQ_String_Real(x,y,string,maxlen,scalex,scaley,red,green,blue,alpha,flags);
627 }
628
629 // color tag printing
630 static vec4_t string_colors[] =
631 {
632         // Quake3 colors
633         // LordHavoc: why on earth is cyan before magenta in Quake3?
634         // LordHavoc: note: Doom3 uses white for [0] and [7]
635         {0.0, 0.0, 0.0, 1.0}, // black
636         {1.0, 0.0, 0.0, 1.0}, // red
637         {0.0, 1.0, 0.0, 1.0}, // green
638         {1.0, 1.0, 0.0, 1.0}, // yellow
639         {0.0, 0.0, 1.0, 1.0}, // blue
640         {0.0, 1.0, 1.0, 1.0}, // cyan
641         {1.0, 0.0, 1.0, 1.0}, // magenta
642         {1.0, 1.0, 1.0, 1.0}, // white
643         // [515]'s BX_COLOREDTEXT extension
644         {1.0, 1.0, 1.0, 0.5}, // half transparent
645         {0.5, 0.5, 0.5, 1.0}  // half brightness
646         // Black's color table
647         //{1.0, 1.0, 1.0, 1.0},
648         //{1.0, 0.0, 0.0, 1.0},
649         //{0.0, 1.0, 0.0, 1.0},
650         //{0.0, 0.0, 1.0, 1.0},
651         //{1.0, 1.0, 0.0, 1.0},
652         //{0.0, 1.0, 1.0, 1.0},
653         //{1.0, 0.0, 1.0, 1.0},
654         //{0.1, 0.1, 0.1, 1.0}
655 };
656
657 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
658
659 // color is read and changed in the end
660 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 )
661 {
662         vec_t *color;
663         int len;
664         int colorindex;
665         const char *start, *current;
666
667         if (!r_refdef.draw2dstage)
668         {
669                 Con_Printf("DrawQ_ColoredString: not in 2d rendering stage!\n");
670                 return;
671         }
672         if( !outcolor || *outcolor == -1 ) {
673                 colorindex = STRING_COLOR_DEFAULT;
674         } else {
675                 colorindex = *outcolor;
676         }
677         color = string_colors[colorindex];
678
679         if( maxlen < 1)
680                 len = (int)strlen( text );
681         else
682                 len = min( maxlen, (int) strlen( text ) );
683
684         start = current = text;
685         while( len > 0 ) {
686                 // check for color control char
687                 if( *current == STRING_COLOR_TAG ) {
688                         // get next char
689                         current++;
690                         len--;
691                         if( len == 0 ) {
692                                 break;
693                         }
694                         // display the tag char?
695                         if( *current == STRING_COLOR_TAG ) {
696                                 // only display one of the two
697                                 start = current;
698                                 // get the next char
699                                 current++;
700                                 len--;
701                         } else if( '0' <= *current && *current <= '9' ) {
702                                 colorindex = 0;
703                                 do {
704                                         colorindex = colorindex * 10 + (*current - '0');
705                                         // only read as long as it makes a valid index
706                                         if( colorindex >= (int)STRING_COLORS_COUNT ) {
707                                                 // undo the last operation
708                                                 colorindex /= 10;
709                                                 break;
710                                         }
711                                         current++;
712                                         len--;
713                                 } while( len > 0 && '0' <= *current && *current <= '9' );
714                                 // set the color
715                                 color = string_colors[colorindex];
716                                 // we jump over the color tag
717                                 start = current;
718                         }
719                 }
720                 // go on and read normal text in until the next control char
721                 while( len > 0 && *current != STRING_COLOR_TAG ) {
722                         current++;
723                         len--;
724                 }
725                 // display the text
726                 if( start != current ) {
727                         // draw the string
728                         DrawQ_String( x, y, start, current - start, scalex, scaley, basered * color[0], basegreen * color[1], baseblue * color[2], basealpha * color[3], flags );
729                         // update x to be at the new start position
730                         x += (current - start) * scalex;
731                         // set start accordingly
732                         start = current;
733                 }
734         }
735
736         // return the last colorindex
737         if( outcolor ) {
738                 *outcolor = colorindex;
739         }
740 }
741
742 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)
743 {
744         float floats[36];
745
746         if (!r_refdef.draw2dstage)
747         {
748                 Con_Printf("DrawQ_SuperPic: not in 2d rendering stage!\n");
749                 return;
750         }
751
752         if (!r_render.integer)
753                 return;
754
755         _DrawQ_ProcessDrawFlag(flags);
756
757         R_Mesh_VertexPointer(floats);
758         R_Mesh_ColorPointer(floats + 20);
759         R_Mesh_ResetTextureState();
760         if (pic)
761         {
762                 if (width == 0)
763                         width = pic->width;
764                 if (height == 0)
765                         height = pic->height;
766                 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
767                 R_Mesh_TexCoordPointer(0, 2, floats + 12);
768                 floats[12] = s1;floats[13] = t1;
769                 floats[14] = s2;floats[15] = t2;
770                 floats[16] = s4;floats[17] = t4;
771                 floats[18] = s3;floats[19] = t3;
772         }
773
774         floats[2] = floats[5] = floats[8] = floats[11] = 0;
775         floats[0] = floats[9] = x;
776         floats[1] = floats[4] = y;
777         floats[3] = floats[6] = x + width;
778         floats[7] = floats[10] = y + height;
779         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
780         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
781         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
782         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
783
784         R_Mesh_Draw(0, 4, 2, polygonelements);
785 }
786
787 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
788 {
789         if (!r_refdef.draw2dstage)
790         {
791                 Con_Printf("DrawQ_Mesh: not in 2d rendering stage!\n");
792                 return;
793         }
794
795         if (!r_render.integer)
796                 return;
797
798         _DrawQ_ProcessDrawFlag(flags);
799
800         R_Mesh_VertexPointer(mesh->data_vertex3f);
801         R_Mesh_ColorPointer(mesh->data_color4f);
802         R_Mesh_ResetTextureState();
803         R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
804         R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f);
805
806         GL_LockArrays(0, mesh->num_vertices);
807         R_Mesh_Draw(0, mesh->num_vertices, mesh->num_triangles, mesh->data_element3i);
808         GL_LockArrays(0, 0);
809 }
810
811 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
812 {
813         int num;
814
815         if (!r_refdef.draw2dstage)
816         {
817                 Con_Printf("DrawQ_LineLoop: not in 2d rendering stage!\n");
818                 return;
819         }
820
821         if (!r_render.integer)
822                 return;
823
824         _DrawQ_ProcessDrawFlag(flags);
825
826         GL_Color(1,1,1,1);
827         CHECKGLERROR
828         qglBegin(GL_LINE_LOOP);
829         for (num = 0;num < mesh->num_vertices;num++)
830         {
831                 if (mesh->data_color4f)
832                         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]);
833                 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
834         }
835         qglEnd();
836         CHECKGLERROR
837 }
838
839 //LordHavoc: FIXME: this is nasty!
840 void DrawQ_LineWidth (float width)
841 {
842         if (!r_refdef.draw2dstage)
843         {
844                 Con_Printf("DrawQ_LineWidth: not in 2d rendering stage!\n");
845                 return;
846         }
847         CHECKGLERROR
848         qglLineWidth(width);CHECKGLERROR
849 }
850
851 //[515]: this is old, delete
852 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
853 {
854         if (!r_refdef.draw2dstage)
855         {
856                 Con_Printf("DrawQ_Line: not in 2d rendering stage!\n");
857                 return;
858         }
859
860         if (!r_render.integer)
861                 return;
862
863         CHECKGLERROR
864         if(width > 0)
865                 DrawQ_LineWidth(width);
866
867         _DrawQ_ProcessDrawFlag(flags);
868
869         GL_Color(r,g,b,alpha);
870         CHECKGLERROR
871         qglBegin(GL_LINES);
872         qglVertex2f(x1, y1);
873         qglVertex2f(x2, y2);
874         qglEnd();
875         CHECKGLERROR
876 }
877
878 void DrawQ_SetClipArea(float x, float y, float width, float height)
879 {
880         if (!r_refdef.draw2dstage)
881         {
882                 Con_Printf("DrawQ_SetClipArea: not in 2d rendering stage!\n");
883                 return;
884         }
885
886         // We have to convert the con coords into real coords
887         // OGL uses top to bottom
888         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)));
889
890         GL_ScissorTest(true);
891 }
892
893 void DrawQ_ResetClipArea(void)
894 {
895         if (!r_refdef.draw2dstage)
896         {
897                 Con_Printf("DrawQ_ResetClipArea: not in 2d rendering stage!\n");
898                 return;
899         }
900         GL_ScissorTest(false);
901 }
902
903 void DrawQ_Finish(void)
904 {
905         if (!r_refdef.draw2dstage)
906         {
907                 Con_Printf("R_DrawQueue: not in 2d rendering stage!\n");
908                 return;
909         }
910
911         r_refdef.draw2dstage = false;
912 }
913
914 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
915 void R_DrawGamma(void)
916 {
917         float c[4];
918         if (!vid_usinghwgamma)
919         {
920                 // all the blends ignore depth
921                 R_Mesh_VertexPointer(blendvertex3f);
922                 R_Mesh_ColorPointer(NULL);
923                 R_Mesh_ResetTextureState();
924                 GL_DepthMask(true);
925                 GL_DepthTest(false);
926                 if (v_color_enable.integer)
927                 {
928                         c[0] = v_color_white_r.value;
929                         c[1] = v_color_white_g.value;
930                         c[2] = v_color_white_b.value;
931                 }
932                 else
933                         c[0] = c[1] = c[2] = v_contrast.value;
934                 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
935                 {
936                         GL_BlendFunc(GL_DST_COLOR, GL_ONE);
937                         while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
938                         {
939                                 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
940                                 R_Mesh_Draw(0, 3, 1, polygonelements);
941                                 VectorScale(c, 0.5, c);
942                         }
943                 }
944                 if (v_color_enable.integer)
945                 {
946                         c[0] = v_color_black_r.value;
947                         c[1] = v_color_black_g.value;
948                         c[2] = v_color_black_b.value;
949                 }
950                 else
951                         c[0] = c[1] = c[2] = v_brightness.value;
952                 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
953                 {
954                         GL_BlendFunc(GL_ONE, GL_ONE);
955                         GL_Color(c[0], c[1], c[2], 1);
956                         R_Mesh_Draw(0, 3, 1, polygonelements);
957                 }
958         }
959 }
960