changed Draw_CachePic to load both tga/png/jpg and lmp/wad images at once and take...
[divverent/darkplaces.git] / gl_draw.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22 #include "image.h"
23 #include "wad.h"
24
25 #include "cl_video.h"
26
27 cvar_t r_textshadow = {0, "r_textshadow", "0", "draws a shadow on all text to improve readability"};
28
29 static rtexture_t *char_texture;
30 cachepic_t *r_crosshairs[NUMCROSSHAIRS+1];
31
32 //=============================================================================
33 /* Support Routines */
34
35 #define FONT_FILESIZE 13468
36 #define MAX_CACHED_PICS 1024
37 #define CACHEPICHASHSIZE 256
38 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
39 static cachepic_t cachepics[MAX_CACHED_PICS];
40 static int numcachepics;
41
42 static rtexturepool_t *drawtexturepool;
43
44 static unsigned char concharimage[FONT_FILESIZE] =
45 {
46 #include "lhfont.h"
47 };
48
49 static rtexture_t *draw_generateconchars(void)
50 {
51         int i;
52         unsigned char buffer[65536][4], *data = NULL;
53         double random;
54
55         data = LoadTGA (concharimage, FONT_FILESIZE, 256, 256);
56 // Gold numbers
57         for (i = 0;i < 8192;i++)
58         {
59                 random = lhrandom (0.0,1.0);
60                 buffer[i][0] = 83 + (unsigned char)(random * 64);
61                 buffer[i][1] = 71 + (unsigned char)(random * 32);
62                 buffer[i][2] = 23 + (unsigned char)(random * 16);
63                 buffer[i][3] = data[i*4+0];
64         }
65 // White chars
66         for (i = 8192;i < 32768;i++)
67         {
68                 random = lhrandom (0.0,1.0);
69                 buffer[i][0] = 95 + (unsigned char)(random * 64);
70                 buffer[i][1] = 95 + (unsigned char)(random * 64);
71                 buffer[i][2] = 95 + (unsigned char)(random * 64);
72                 buffer[i][3] = data[i*4+0];
73         }
74 // Gold numbers
75         for (i = 32768;i < 40960;i++)
76         {
77                 random = lhrandom (0.0,1.0);
78                 buffer[i][0] = 83 + (unsigned char)(random * 64);
79                 buffer[i][1] = 71 + (unsigned char)(random * 32);
80                 buffer[i][2] = 23 + (unsigned char)(random * 16);
81                 buffer[i][3] = data[i*4+0];
82         }
83 // Red chars
84         for (i = 40960;i < 65536;i++)
85         {
86                 random = lhrandom (0.0,1.0);
87                 buffer[i][0] = 96 + (unsigned char)(random * 64);
88                 buffer[i][1] = 43 + (unsigned char)(random * 32);
89                 buffer[i][2] = 27 + (unsigned char)(random * 32);
90                 buffer[i][3] = data[i*4+0];
91         }
92
93 #if 0
94         Image_WriteTGARGBA ("gfx/generated_conchars.tga", 256, 256, &buffer[0][0]);
95 #endif
96
97         Mem_Free(data);
98         return R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, &buffer[0][0], TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
99 }
100
101 static char *pointerimage =
102         "333333332......."
103         "26777761........"
104         "2655541........."
105         "265541.........."
106         "2654561........."
107         "26414561........"
108         "251.14561......."
109         "21...14561......"
110         "1.....141......."
111         ".......1........"
112         "................"
113         "................"
114         "................"
115         "................"
116         "................"
117         "................"
118 ;
119
120 static rtexture_t *draw_generatemousepointer(void)
121 {
122         int i;
123         unsigned char buffer[256][4];
124         for (i = 0;i < 256;i++)
125         {
126                 if (pointerimage[i] == '.')
127                 {
128                         buffer[i][0] = 0;
129                         buffer[i][1] = 0;
130                         buffer[i][2] = 0;
131                         buffer[i][3] = 0;
132                 }
133                 else
134                 {
135                         buffer[i][0] = (pointerimage[i] - '0') * 16;
136                         buffer[i][1] = (pointerimage[i] - '0') * 16;
137                         buffer[i][2] = (pointerimage[i] - '0') * 16;
138                         buffer[i][3] = 255;
139                 }
140         }
141         return R_LoadTexture2D(drawtexturepool, "mousepointer", 16, 16, &buffer[0][0], TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
142 }
143
144 static char *crosshairtexdata[NUMCROSSHAIRS] =
145 {
146         "................"
147         "................"
148         "................"
149         "...33......33..."
150         "...355....553..."
151         "....577..775...."
152         ".....77..77....."
153         "................"
154         "................"
155         ".....77..77....."
156         "....577..775...."
157         "...355....553..."
158         "...33......33..."
159         "................"
160         "................"
161         "................"
162         ,
163         "................"
164         "................"
165         "................"
166         "...3........3..."
167         "....5......5...."
168         ".....7....7....."
169         "......7..7......"
170         "................"
171         "................"
172         "......7..7......"
173         ".....7....7....."
174         "....5......5...."
175         "...3........3..."
176         "................"
177         "................"
178         "................"
179         ,
180         "................"
181         ".......77......."
182         ".......77......."
183         "................"
184         "................"
185         ".......44......."
186         ".......44......."
187         ".77..44..44..77."
188         ".77..44..44..77."
189         ".......44......."
190         ".......44......."
191         "................"
192         "................"
193         ".......77......."
194         ".......77......."
195         "................"
196         ,
197         "................"
198         "................"
199         "................"
200         "................"
201         "................"
202         "................"
203         "................"
204         "................"
205         "........7777777."
206         "........752....."
207         "........72......"
208         "........7......."
209         "........7......."
210         "........7......."
211         "........7......."
212         "................"
213         ,
214         "................"
215         "................"
216         "................"
217         "................"
218         "................"
219         "........7......."
220         "................"
221         "........4......."
222         ".....7.4.4.7...."
223         "........4......."
224         "................"
225         "........7......."
226         "................"
227         "................"
228         "................"
229         "................"
230         ,
231         "................"
232         "................"
233         "................"
234         "................"
235         "................"
236         "................"
237         "................"
238         ".......55......."
239         ".......55......."
240         "................"
241         "................"
242         "................"
243         "................"
244         "................"
245         "................"
246         "................"
247 };
248
249 static rtexture_t *draw_generatecrosshair(int num)
250 {
251         int i;
252         char *in;
253         unsigned char data[16*16][4];
254         in = crosshairtexdata[num];
255         for (i = 0;i < 16*16;i++)
256         {
257                 if (in[i] == '.')
258                 {
259                         data[i][0] = 255;
260                         data[i][1] = 255;
261                         data[i][2] = 255;
262                         data[i][3] = 0;
263                 }
264                 else
265                 {
266                         data[i][0] = data[i][1] = data[i][2] = (unsigned char) ((int) (in[i] - '0') * 127 / 7 + 128);
267                         data[i][3] = 255;
268                 }
269         }
270         return R_LoadTexture2D(drawtexturepool, va("crosshair%i", num), 16, 16, &data[0][0], TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
271 }
272
273 static rtexture_t *draw_generateditherpattern(void)
274 {
275 #if 1
276         int x, y;
277         unsigned char data[8*8*4];
278         for (y = 0;y < 8;y++)
279         {
280                 for (x = 0;x < 8;x++)
281                 {
282                         data[(y*8+x)*4+0] = data[(y*8+x)*4+1] = data[(y*8+x)*4+2] = ((x^y) & 4) ? 255 : 0;
283                         data[(y*8+x)*4+3] = 255;
284                 }
285         }
286         return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, data, TEXTYPE_RGBA, TEXF_FORCENEAREST | TEXF_PRECACHE, NULL);
287 #else
288         unsigned char data[16];
289         memset(data, 255, sizeof(data));
290         data[0] = data[1] = data[2] = data[12] = data[13] = data[14] = 0;
291         return R_LoadTexture2D(drawtexturepool, "ditherpattern", 2, 2, data, TEXTYPE_RGBA, TEXF_FORCENEAREST | TEXF_PRECACHE, NULL);
292 #endif
293 }
294
295 /*
296 ================
297 Draw_CachePic
298 ================
299 */
300 // FIXME: move this to client somehow
301 cachepic_t      *Draw_CachePic (const char *path, qboolean persistent)
302 {
303         int crc, hashkey;
304         cachepic_t *pic;
305         int flags;
306         fs_offset_t lmpsize;
307         unsigned char *lmpdata;
308         char lmpname[MAX_QPATH];
309
310         if (!strncmp(CLVIDEOPREFIX, path, sizeof(CLVIDEOPREFIX) - 1))
311         {
312                 clvideo_t *video;
313
314                 video = CL_GetVideoByName(path);
315                 if( video )
316                         return &video->cpif;
317         }
318
319         crc = CRC_Block((unsigned char *)path, strlen(path));
320         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
321         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
322                 if (!strcmp (path, pic->name))
323                         return pic;
324
325         if (numcachepics == MAX_CACHED_PICS)
326         {
327                 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
328                 // FIXME: support NULL in callers?
329                 return cachepics; // return the first one
330         }
331         pic = cachepics + (numcachepics++);
332         strlcpy (pic->name, path, sizeof(pic->name));
333         // link into list
334         pic->chain = cachepichash[hashkey];
335         cachepichash[hashkey] = pic;
336
337         flags = TEXF_ALPHA;
338         if (persistent)
339                 flags |= TEXF_PRECACHE;
340         if (!strcmp(path, "gfx/colorcontrol/ditherpattern"))
341                 flags |= TEXF_CLAMP;
342
343         // load a high quality image from disk if possible
344         pic->tex = loadtextureimage(drawtexturepool, path, 0, 0, false, flags);
345         if (pic->tex == NULL && !strncmp(path, "gfx/", 4))
346         {
347                 // compatibility with older versions which did not require gfx/ prefix
348                 pic->tex = loadtextureimage(drawtexturepool, path + 4, 0, 0, false, flags);
349         }
350         // if a high quality image was loaded, set the pic's size to match it, just
351         // in case there's no low quality version to get the size from
352         if (pic->tex)
353         {
354                 pic->width = R_TextureWidth(pic->tex);
355                 pic->height = R_TextureHeight(pic->tex);
356         }
357
358         // now read the low quality version (wad or lmp file), and take the pic
359         // size from that even if we don't upload the texture, this way the pics
360         // show up the right size in the menu even if they were replaced with
361         // higher or lower resolution versions
362         dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
363         if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
364         {
365                 if (lmpsize >= 9)
366                 {
367                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
368                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
369                         // if no high quality replacement image was found, upload the original low quality texture
370                         if (!pic->tex)
371                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags, palette_transparent);
372                 }
373                 Mem_Free(lmpdata);
374         }
375         else if ((lmpdata = W_GetLumpName (path + 4)))
376         {
377                 if (!strcmp(path, "gfx/conchars"))
378                 {
379                         // conchars is a raw image and with color 0 as transparent instead of 255
380                         pic->width = 128;
381                         pic->height = 128;
382                         // if no high quality replacement image was found, upload the original low quality texture
383                         if (!pic->tex)
384                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, flags, palette_font);
385                 }
386                 else
387                 {
388                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
389                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
390                         // if no high quality replacement image was found, upload the original low quality texture
391                         if (!pic->tex)
392                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags, palette_transparent);
393                 }
394         }
395
396         // if it's not found on disk, check if it's one of the builtin images
397         if (pic->tex == NULL)
398         {
399                 if (pic->tex == NULL && !strcmp(path, "gfx/conchars"))
400                         pic->tex = draw_generateconchars();
401                 if (pic->tex == NULL && !strcmp(path, "ui/mousepointer"))
402                         pic->tex = draw_generatemousepointer();
403                 if (pic->tex == NULL && !strcmp(path, "gfx/prydoncursor001"))
404                         pic->tex = draw_generatemousepointer();
405                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair1"))
406                         pic->tex = draw_generatecrosshair(0);
407                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair2"))
408                         pic->tex = draw_generatecrosshair(1);
409                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair3"))
410                         pic->tex = draw_generatecrosshair(2);
411                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair4"))
412                         pic->tex = draw_generatecrosshair(3);
413                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair5"))
414                         pic->tex = draw_generatecrosshair(4);
415                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair6"))
416                         pic->tex = draw_generatecrosshair(5);
417                 if (pic->tex == NULL && !strcmp(path, "gfx/colorcontrol/ditherpattern"))
418                         pic->tex = draw_generateditherpattern();
419                 if (pic->tex == NULL)
420                 {
421                         Con_Printf("Draw_CachePic: failed to load %s\n", path);
422                         pic->tex = r_texture_notexture;
423                 }
424                 pic->width = R_TextureWidth(pic->tex);
425                 pic->height = R_TextureHeight(pic->tex);
426         }
427
428         return pic;
429 }
430
431 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels)
432 {
433         int crc, hashkey;
434         cachepic_t *pic;
435
436         crc = CRC_Block((unsigned char *)picname, strlen(picname));
437         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
438         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
439                 if (!strcmp (picname, pic->name))
440                         break;
441
442         if (pic)
443         {
444                 if (pic->tex && pic->width == width && pic->height == height)
445                 {
446                         R_UpdateTexture(pic->tex, pixels, 0, 0, width, height);
447                         return pic;
448                 }
449         }
450         else
451         {
452                 if (pic == NULL)
453                 {
454                         if (numcachepics == MAX_CACHED_PICS)
455                         {
456                                 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
457                                 // FIXME: support NULL in callers?
458                                 return cachepics; // return the first one
459                         }
460                         pic = cachepics + (numcachepics++);
461                         strcpy (pic->name, picname);
462                         // link into list
463                         pic->chain = cachepichash[hashkey];
464                         cachepichash[hashkey] = pic;
465                 }
466         }
467
468         pic->width = width;
469         pic->height = height;
470         if (pic->tex)
471                 R_FreeTexture(pic->tex);
472         pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels, TEXTYPE_RGBA, alpha ? TEXF_ALPHA : 0, NULL);
473         return pic;
474 }
475
476 void Draw_FreePic(const char *picname)
477 {
478         int crc;
479         int hashkey;
480         cachepic_t *pic;
481         // this doesn't really free the pic, but does free it's texture
482         crc = CRC_Block((unsigned char *)picname, strlen(picname));
483         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
484         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
485         {
486                 if (!strcmp (picname, pic->name) && pic->tex)
487                 {
488                         R_FreeTexture(pic->tex);
489                         pic->width = 0;
490                         pic->height = 0;
491                         return;
492                 }
493         }
494 }
495
496 /*
497 ===============
498 Draw_Init
499 ===============
500 */
501 static void gl_draw_start(void)
502 {
503         int i;
504         drawtexturepool = R_AllocTexturePool();
505
506         numcachepics = 0;
507         memset(cachepichash, 0, sizeof(cachepichash));
508
509         char_texture = Draw_CachePic("gfx/conchars", true)->tex;
510         for (i = 1;i <= NUMCROSSHAIRS;i++)
511                 r_crosshairs[i] = Draw_CachePic(va("gfx/crosshair%i", i), true);
512 }
513
514 static void gl_draw_shutdown(void)
515 {
516         R_FreeTexturePool(&drawtexturepool);
517
518         numcachepics = 0;
519         memset(cachepichash, 0, sizeof(cachepichash));
520 }
521
522 static void gl_draw_newmap(void)
523 {
524 }
525
526 void GL_Draw_Init (void)
527 {
528         Cvar_RegisterVariable(&r_textshadow);
529         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
530 }
531
532 void DrawQ_Begin(void)
533 {
534         GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
535
536         CHECKGLERROR
537         qglViewport(r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
538         GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
539         qglDepthFunc(GL_LEQUAL);CHECKGLERROR
540         R_Mesh_Matrix(&identitymatrix);
541
542         GL_DepthMask(true);
543         GL_DepthTest(false);
544         GL_Color(1,1,1,1);
545         GL_AlphaTest(false);
546         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
547
548         r_refdef.draw2dstage = true;
549 }
550
551 static void _DrawQ_ProcessDrawFlag(int flags)
552 {
553         CHECKGLERROR
554         if(flags == DRAWFLAG_ADDITIVE)
555                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
556         else if(flags == DRAWFLAG_MODULATE)
557                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
558         else if(flags == DRAWFLAG_2XMODULATE)
559                 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
560         else
561                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
562 }
563
564 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
565 {
566         if (!r_refdef.draw2dstage)
567         {
568                 Con_Printf("DrawQ_Pic: not in 2d rendering stage!\n");
569                 return;
570         }
571         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);
572 }
573
574 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)
575 {
576         int i, num;
577         float *av, *at;
578         int batchcount;
579         float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
580         float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
581
582         if (!r_refdef.draw2dstage)
583         {
584                 Con_Printf("DrawQ_String: not in 2d rendering stage!\n");
585                 return;
586         }
587
588         if (alpha < (1.0f / 255.0f))
589                 return;
590
591         _DrawQ_ProcessDrawFlag(flags);
592
593         GL_Color(red, green, blue, alpha);
594
595         R_Mesh_VertexPointer(vertex3f);
596         R_Mesh_ColorPointer(NULL);
597         R_Mesh_ResetTextureState();
598         R_Mesh_TexBind(0, R_GetTexture(char_texture));
599         R_Mesh_TexCoordPointer(0, 2, texcoord2f);
600
601         at = texcoord2f;
602         av = vertex3f;
603         batchcount = 0;
604
605         if (maxlen < 1)
606                 maxlen = 9999;
607         for (i = 0;i < maxlen && x < vid_conwidth.integer && (num = string[i]);i++, x += w)
608         {
609                 float s, t, u, v;
610                 if (num == ' ')
611                         continue;
612                 s = (num & 15)*0.0625f + (0.5f / 256.0f);
613                 t = (num >> 4)*0.0625f + (0.5f / 256.0f);
614                 u = 0.0625f - (1.0f / 256.0f);
615                 v = 0.0625f - (1.0f / 256.0f);
616                 at[ 0] = s  ;at[ 1] = t  ;
617                 at[ 2] = s+u;at[ 3] = t  ;
618                 at[ 4] = s+u;at[ 5] = t+v;
619                 at[ 6] = s  ;at[ 7] = t+v;
620                 av[ 0] = x  ;av[ 1] = y  ;av[ 2] = 10;
621                 av[ 3] = x+w;av[ 4] = y  ;av[ 5] = 10;
622                 av[ 6] = x+w;av[ 7] = y+h;av[ 8] = 10;
623                 av[ 9] = x  ;av[10] = y+h;av[11] = 10;
624                 at += 8;
625                 av += 12;
626                 batchcount++;
627                 if (batchcount >= QUADELEMENTS_MAXQUADS)
628                 {
629                         GL_LockArrays(0, batchcount * 4);
630                         R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements);
631                         GL_LockArrays(0, 0);
632                         batchcount = 0;
633                         at = texcoord2f;
634                         av = vertex3f;
635                 }
636         }
637         if (batchcount > 0)
638         {
639                 GL_LockArrays(0, batchcount * 4);
640                 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements);
641                 GL_LockArrays(0, 0);
642         }
643 }
644
645 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)
646 {
647         if (!r_refdef.draw2dstage)
648         {
649                 Con_Printf("DrawQ_String: not in 2d rendering stage!\n");
650                 return;
651         }
652
653         if (r_textshadow.integer)
654                 DrawQ_String_Real(x+scalex*0.25,y+scaley*0.25,string,maxlen,scalex,scaley,0,0,0,alpha*0.8,flags);
655
656         DrawQ_String_Real(x,y,string,maxlen,scalex,scaley,red,green,blue,alpha,flags);
657 }
658
659 // color tag printing
660 static vec4_t string_colors[] =
661 {
662         // Quake3 colors
663         // LordHavoc: why on earth is cyan before magenta in Quake3?
664         // LordHavoc: note: Doom3 uses white for [0] and [7]
665         {0.0, 0.0, 0.0, 1.0}, // black
666         {1.0, 0.0, 0.0, 1.0}, // red
667         {0.0, 1.0, 0.0, 1.0}, // green
668         {1.0, 1.0, 0.0, 1.0}, // yellow
669         {0.0, 0.0, 1.0, 1.0}, // blue
670         {0.0, 1.0, 1.0, 1.0}, // cyan
671         {1.0, 0.0, 1.0, 1.0}, // magenta
672         {1.0, 1.0, 1.0, 1.0}, // white
673         // [515]'s BX_COLOREDTEXT extension
674         {1.0, 1.0, 1.0, 0.5}, // half transparent
675         {0.5, 0.5, 0.5, 1.0}  // half brightness
676         // Black's color table
677         //{1.0, 1.0, 1.0, 1.0},
678         //{1.0, 0.0, 0.0, 1.0},
679         //{0.0, 1.0, 0.0, 1.0},
680         //{0.0, 0.0, 1.0, 1.0},
681         //{1.0, 1.0, 0.0, 1.0},
682         //{0.0, 1.0, 1.0, 1.0},
683         //{1.0, 0.0, 1.0, 1.0},
684         //{0.1, 0.1, 0.1, 1.0}
685 };
686
687 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
688
689 // color is read and changed in the end
690 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 )
691 {
692         vec_t *color;
693         int len;
694         int colorindex;
695         const char *start, *current;
696
697         if (!r_refdef.draw2dstage)
698         {
699                 Con_Printf("DrawQ_ColoredString: not in 2d rendering stage!\n");
700                 return;
701         }
702         if( !outcolor || *outcolor == -1 ) {
703                 colorindex = STRING_COLOR_DEFAULT;
704         } else {
705                 colorindex = *outcolor;
706         }
707         color = string_colors[colorindex];
708
709         if( maxlen < 1)
710                 len = (int)strlen( text );
711         else
712                 len = min( maxlen, (int) strlen( text ) );
713
714         start = current = text;
715         while( len > 0 ) {
716                 // check for color control char
717                 if( *current == STRING_COLOR_TAG ) {
718                         // get next char
719                         current++;
720                         len--;
721                         if( len == 0 ) {
722                                 break;
723                         }
724                         // display the tag char?
725                         if( *current == STRING_COLOR_TAG ) {
726                                 // only display one of the two
727                                 start = current;
728                                 // get the next char
729                                 current++;
730                                 len--;
731                         } else if( '0' <= *current && *current <= '9' ) {
732                                 colorindex = 0;
733                                 do {
734                                         colorindex = colorindex * 10 + (*current - '0');
735                                         // only read as long as it makes a valid index
736                                         if( colorindex >= (int)STRING_COLORS_COUNT ) {
737                                                 // undo the last operation
738                                                 colorindex /= 10;
739                                                 break;
740                                         }
741                                         current++;
742                                         len--;
743                                 } while( len > 0 && '0' <= *current && *current <= '9' );
744                                 // set the color
745                                 color = string_colors[colorindex];
746                                 // we jump over the color tag
747                                 start = current;
748                         }
749                 }
750                 // go on and read normal text in until the next control char
751                 while( len > 0 && *current != STRING_COLOR_TAG ) {
752                         current++;
753                         len--;
754                 }
755                 // display the text
756                 if( start != current ) {
757                         // draw the string
758                         DrawQ_String( x, y, start, current - start, scalex, scaley, basered * color[0], basegreen * color[1], baseblue * color[2], basealpha * color[3], flags );
759                         // update x to be at the new start position
760                         x += (current - start) * scalex;
761                         // set start accordingly
762                         start = current;
763                 }
764         }
765
766         // return the last colorindex
767         if( outcolor ) {
768                 *outcolor = colorindex;
769         }
770 }
771
772 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)
773 {
774         float floats[36];
775
776         if (!r_refdef.draw2dstage)
777         {
778                 Con_Printf("DrawQ_SuperPic: not in 2d rendering stage!\n");
779                 return;
780         }
781
782         _DrawQ_ProcessDrawFlag(flags);
783
784         R_Mesh_VertexPointer(floats);
785         R_Mesh_ColorPointer(floats + 20);
786         R_Mesh_ResetTextureState();
787         if (pic)
788         {
789                 if (width == 0)
790                         width = pic->width;
791                 if (height == 0)
792                         height = pic->height;
793                 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
794                 R_Mesh_TexCoordPointer(0, 2, floats + 12);
795                 floats[12] = s1;floats[13] = t1;
796                 floats[14] = s2;floats[15] = t2;
797                 floats[16] = s4;floats[17] = t4;
798                 floats[18] = s3;floats[19] = t3;
799         }
800
801         floats[2] = floats[5] = floats[8] = floats[11] = 0;
802         floats[0] = floats[9] = x;
803         floats[1] = floats[4] = y;
804         floats[3] = floats[6] = x + width;
805         floats[7] = floats[10] = y + height;
806         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
807         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
808         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
809         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
810
811         R_Mesh_Draw(0, 4, 2, polygonelements);
812 }
813
814 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
815 {
816         if (!r_refdef.draw2dstage)
817         {
818                 Con_Printf("DrawQ_Mesh: not in 2d rendering stage!\n");
819                 return;
820         }
821
822         _DrawQ_ProcessDrawFlag(flags);
823
824         R_Mesh_VertexPointer(mesh->data_vertex3f);
825         R_Mesh_ColorPointer(mesh->data_color4f);
826         R_Mesh_ResetTextureState();
827         R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
828         R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f);
829
830         GL_LockArrays(0, mesh->num_vertices);
831         R_Mesh_Draw(0, mesh->num_vertices, mesh->num_triangles, mesh->data_element3i);
832         GL_LockArrays(0, 0);
833 }
834
835 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
836 {
837         int num;
838
839         if (!r_refdef.draw2dstage)
840         {
841                 Con_Printf("DrawQ_LineLoop: not in 2d rendering stage!\n");
842                 return;
843         }
844
845         _DrawQ_ProcessDrawFlag(flags);
846
847         GL_Color(1,1,1,1);
848         CHECKGLERROR
849         qglBegin(GL_LINE_LOOP);
850         for (num = 0;num < mesh->num_vertices;num++)
851         {
852                 if (mesh->data_color4f)
853                         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]);
854                 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
855         }
856         qglEnd();
857         CHECKGLERROR
858 }
859
860 //LordHavoc: FIXME: this is nasty!
861 void DrawQ_LineWidth (float width)
862 {
863         if (!r_refdef.draw2dstage)
864         {
865                 Con_Printf("DrawQ_LineWidth: not in 2d rendering stage!\n");
866                 return;
867         }
868         CHECKGLERROR
869         qglLineWidth(width);CHECKGLERROR
870 }
871
872 //[515]: this is old, delete
873 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
874 {
875         if (!r_refdef.draw2dstage)
876         {
877                 Con_Printf("DrawQ_Line: not in 2d rendering stage!\n");
878                 return;
879         }
880
881         CHECKGLERROR
882         if(width > 0)
883                 DrawQ_LineWidth(width);
884
885         _DrawQ_ProcessDrawFlag(flags);
886
887         GL_Color(r,g,b,alpha);
888         CHECKGLERROR
889         qglBegin(GL_LINES);
890         qglVertex2f(x1, y1);
891         qglVertex2f(x2, y2);
892         qglEnd();
893         CHECKGLERROR
894 }
895
896 void DrawQ_SetClipArea(float x, float y, float width, float height)
897 {
898         if (!r_refdef.draw2dstage)
899         {
900                 Con_Printf("DrawQ_SetClipArea: not in 2d rendering stage!\n");
901                 return;
902         }
903
904         // We have to convert the con coords into real coords
905         // OGL uses top to bottom
906         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)));
907
908         GL_ScissorTest(true);
909 }
910
911 void DrawQ_ResetClipArea(void)
912 {
913         if (!r_refdef.draw2dstage)
914         {
915                 Con_Printf("DrawQ_ResetClipArea: not in 2d rendering stage!\n");
916                 return;
917         }
918         GL_ScissorTest(false);
919 }
920
921 void DrawQ_Finish(void)
922 {
923         if (!r_refdef.draw2dstage)
924         {
925                 Con_Printf("R_DrawQueue: not in 2d rendering stage!\n");
926                 return;
927         }
928
929         r_refdef.draw2dstage = false;
930 }
931
932 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
933 void R_DrawGamma(void)
934 {
935         float c[4];
936         if (!vid_usinghwgamma)
937         {
938                 // all the blends ignore depth
939                 R_Mesh_VertexPointer(blendvertex3f);
940                 R_Mesh_ColorPointer(NULL);
941                 R_Mesh_ResetTextureState();
942                 GL_DepthMask(true);
943                 GL_DepthTest(false);
944                 if (v_color_enable.integer)
945                 {
946                         c[0] = v_color_white_r.value;
947                         c[1] = v_color_white_g.value;
948                         c[2] = v_color_white_b.value;
949                 }
950                 else
951                         c[0] = c[1] = c[2] = v_contrast.value;
952                 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
953                 {
954                         GL_BlendFunc(GL_DST_COLOR, GL_ONE);
955                         while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
956                         {
957                                 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
958                                 R_Mesh_Draw(0, 3, 1, polygonelements);
959                                 VectorScale(c, 0.5, c);
960                         }
961                 }
962                 if (v_color_enable.integer)
963                 {
964                         c[0] = v_color_black_r.value;
965                         c[1] = v_color_black_g.value;
966                         c[2] = v_color_black_b.value;
967                 }
968                 else
969                         c[0] = c[1] = c[2] = v_brightness.value;
970                 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
971                 {
972                         GL_BlendFunc(GL_ONE, GL_ONE);
973                         GL_Color(c[0], c[1], c[2], 1);
974                         R_Mesh_Draw(0, 3, 1, polygonelements);
975                 }
976         }
977 }
978