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