update docs for dp_reflect, dp_refract, dp_water
[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 #include "cl_dyntexture.h"
27
28 dp_font_t dp_fonts[MAX_FONTS] = {{0}};
29
30 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)"};
31 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)"};
32 cvar_t r_textcontrast = {CVAR_SAVE, "r_textcontrast", "1", "additional contrast for text color codes (1 keeps colors as is, 1 makes them all black)"};
33
34 //=============================================================================
35 /* Support Routines */
36
37 #define FONT_FILESIZE 13468
38 #define MAX_CACHED_PICS 1024
39 #define CACHEPICHASHSIZE 256
40 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
41 static cachepic_t cachepics[MAX_CACHED_PICS];
42 static int numcachepics;
43
44 static rtexturepool_t *drawtexturepool;
45
46 static const unsigned char concharimage[FONT_FILESIZE] =
47 {
48 #include "lhfont.h"
49 };
50
51 static rtexture_t *draw_generateconchars(void)
52 {
53         int i;
54         unsigned char buffer[65536][4], *data = NULL;
55         double random;
56
57         data = LoadTGA_BGRA (concharimage, FONT_FILESIZE);
58 // Gold numbers
59         for (i = 0;i < 8192;i++)
60         {
61                 random = lhrandom (0.0,1.0);
62                 buffer[i][2] = 83 + (unsigned char)(random * 64);
63                 buffer[i][1] = 71 + (unsigned char)(random * 32);
64                 buffer[i][0] = 23 + (unsigned char)(random * 16);
65                 buffer[i][3] = data[i*4+0];
66         }
67 // White chars
68         for (i = 8192;i < 32768;i++)
69         {
70                 random = lhrandom (0.0,1.0);
71                 buffer[i][2] = 95 + (unsigned char)(random * 64);
72                 buffer[i][1] = 95 + (unsigned char)(random * 64);
73                 buffer[i][0] = 95 + (unsigned char)(random * 64);
74                 buffer[i][3] = data[i*4+0];
75         }
76 // Gold numbers
77         for (i = 32768;i < 40960;i++)
78         {
79                 random = lhrandom (0.0,1.0);
80                 buffer[i][2] = 83 + (unsigned char)(random * 64);
81                 buffer[i][1] = 71 + (unsigned char)(random * 32);
82                 buffer[i][0] = 23 + (unsigned char)(random * 16);
83                 buffer[i][3] = data[i*4+0];
84         }
85 // Red chars
86         for (i = 40960;i < 65536;i++)
87         {
88                 random = lhrandom (0.0,1.0);
89                 buffer[i][2] = 96 + (unsigned char)(random * 64);
90                 buffer[i][1] = 43 + (unsigned char)(random * 32);
91                 buffer[i][0] = 27 + (unsigned char)(random * 32);
92                 buffer[i][3] = data[i*4+0];
93         }
94
95 #if 0
96         Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, &buffer[0][0]);
97 #endif
98
99         Mem_Free(data);
100         return R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, &buffer[0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
101 }
102
103 static rtexture_t *draw_generateditherpattern(void)
104 {
105         int x, y;
106         unsigned char pixels[8][8];
107         for (y = 0;y < 8;y++)
108                 for (x = 0;x < 8;x++)
109                         pixels[y][x] = ((x^y) & 4) ? 254 : 0;
110         return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST | TEXF_PRECACHE, palette_bgra_transparent);
111 }
112
113 typedef struct embeddedpic_s
114 {
115         const char *name;
116         int width;
117         int height;
118         const char *pixels;
119 }
120 embeddedpic_t;
121
122 static const embeddedpic_t embeddedpics[] =
123 {
124         {
125         "gfx/prydoncursor001", 16, 16,
126         "477777774......."
127         "77.....6........"
128         "7.....6........."
129         "7....6.........."
130         "7.....6........."
131         "7..6...6........"
132         "7.6.6...6......."
133         "76...6...6......"
134         "4.....6.6......."
135         ".......6........"
136         "................"
137         "................"
138         "................"
139         "................"
140         "................"
141         "................"
142         },
143         {
144         "ui/mousepointer", 16, 16,
145         "477777774......."
146         "77.....6........"
147         "7.....6........."
148         "7....6.........."
149         "7.....6........."
150         "7..6...6........"
151         "7.6.6...6......."
152         "76...6...6......"
153         "4.....6.6......."
154         ".......6........"
155         "................"
156         "................"
157         "................"
158         "................"
159         "................"
160         "................"
161         },
162         {
163         "gfx/crosshair1", 16, 16,
164         "................"
165         "................"
166         "................"
167         "...33......33..."
168         "...355....553..."
169         "....577..775...."
170         ".....77..77....."
171         "................"
172         "................"
173         ".....77..77....."
174         "....577..775...."
175         "...355....553..."
176         "...33......33..."
177         "................"
178         "................"
179         "................"
180         },
181         {
182         "gfx/crosshair2", 16, 16,
183         "................"
184         "................"
185         "................"
186         "...3........3..."
187         "....5......5...."
188         ".....7....7....."
189         "......7..7......"
190         "................"
191         "................"
192         "......7..7......"
193         ".....7....7....."
194         "....5......5...."
195         "...3........3..."
196         "................"
197         "................"
198         "................"
199         },
200         {
201         "gfx/crosshair3", 16, 16,
202         "................"
203         ".......77......."
204         ".......77......."
205         "................"
206         "................"
207         ".......44......."
208         ".......44......."
209         ".77..44..44..77."
210         ".77..44..44..77."
211         ".......44......."
212         ".......44......."
213         "................"
214         "................"
215         ".......77......."
216         ".......77......."
217         "................"
218         },
219         {
220         "gfx/crosshair4", 16, 16,
221         "................"
222         "................"
223         "................"
224         "................"
225         "................"
226         "................"
227         "................"
228         "................"
229         "........7777777."
230         "........752....."
231         "........72......"
232         "........7......."
233         "........7......."
234         "........7......."
235         "........7......."
236         "................"
237         },
238         {
239         "gfx/crosshair5", 8, 8,
240         "........"
241         "........"
242         "....7..."
243         "........"
244         "..7.7.7."
245         "........"
246         "....7..."
247         "........"
248         },
249         {
250         "gfx/crosshair6", 2, 2,
251         "77"
252         "77"
253         },
254         {
255         "gfx/crosshair7", 16, 16,
256         "................"
257         ".3............3."
258         "..5...2332...5.."
259         "...7.3....3.7..."
260         "....7......7...."
261         "...3.7....7.3..."
262         "..2...7..7...2.."
263         "..3..........3.."
264         "..3..........3.."
265         "..2...7..7...2.."
266         "...3.7....7.3..."
267         "....7......7...."
268         "...7.3....3.7..."
269         "..5...2332...5.."
270         ".3............3."
271         "................"
272         },
273         {
274         "gfx/editlights/cursor", 16, 16,
275         "................"
276         ".3............3."
277         "..5...2332...5.."
278         "...7.3....3.7..."
279         "....7......7...."
280         "...3.7....7.3..."
281         "..2...7..7...2.."
282         "..3..........3.."
283         "..3..........3.."
284         "..2...7..7...2.."
285         "...3.7....7.3..."
286         "....7......7...."
287         "...7.3....3.7..."
288         "..5...2332...5.."
289         ".3............3."
290         "................"
291         },
292         {
293         "gfx/editlights/light", 16, 16,
294         "................"
295         "................"
296         "......1111......"
297         "....11233211...."
298         "...1234554321..."
299         "...1356776531..."
300         "..124677776421.."
301         "..135777777531.."
302         "..135777777531.."
303         "..124677776421.."
304         "...1356776531..."
305         "...1234554321..."
306         "....11233211...."
307         "......1111......"
308         "................"
309         "................"
310         },
311         {
312         "gfx/editlights/noshadow", 16, 16,
313         "................"
314         "................"
315         "......1111......"
316         "....11233211...."
317         "...1234554321..."
318         "...1356226531..."
319         "..12462..26421.."
320         "..1352....2531.."
321         "..1352....2531.."
322         "..12462..26421.."
323         "...1356226531..."
324         "...1234554321..."
325         "....11233211...."
326         "......1111......"
327         "................"
328         "................"
329         },
330         {
331         "gfx/editlights/selection", 16, 16,
332         "................"
333         ".777752..257777."
334         ".742........247."
335         ".72..........27."
336         ".7............7."
337         ".5............5."
338         ".2............2."
339         "................"
340         "................"
341         ".2............2."
342         ".5............5."
343         ".7............7."
344         ".72..........27."
345         ".742........247."
346         ".777752..257777."
347         "................"
348         },
349         {
350         "gfx/editlights/cubemaplight", 16, 16,
351         "................"
352         "................"
353         "......2772......"
354         "....27755772...."
355         "..277533335772.."
356         "..753333333357.."
357         "..777533335777.."
358         "..735775577537.."
359         "..733357753337.."
360         "..733337733337.."
361         "..753337733357.."
362         "..277537735772.."
363         "....27777772...."
364         "......2772......"
365         "................"
366         "................"
367         },
368         {
369         "gfx/editlights/cubemapnoshadowlight", 16, 16,
370         "................"
371         "................"
372         "......2772......"
373         "....27722772...."
374         "..2772....2772.."
375         "..72........27.."
376         "..7772....2777.."
377         "..7.27722772.7.."
378         "..7...2772...7.."
379         "..7....77....7.."
380         "..72...77...27.."
381         "..2772.77.2772.."
382         "....27777772...."
383         "......2772......"
384         "................"
385         "................"
386         },
387         {NULL, 0, 0, NULL}
388 };
389
390 static rtexture_t *draw_generatepic(const char *name)
391 {
392         const embeddedpic_t *p;
393         for (p = embeddedpics;p->name;p++)
394                 if (!strcmp(name, p->name))
395                         return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA | TEXF_PRECACHE, palette_bgra_embeddedpic);
396         if (!strcmp(name, "gfx/conchars"))
397                 return draw_generateconchars();
398         if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
399                 return draw_generateditherpattern();
400         Con_Printf("Draw_CachePic: failed to load %s\n", name);
401         return r_texture_notexture;
402 }
403
404
405 /*
406 ================
407 Draw_CachePic
408 ================
409 */
410 // FIXME: move this to client somehow
411 static cachepic_t       *Draw_CachePic_Compression (const char *path, qboolean persistent, qboolean allow_compression)
412 {
413         int crc, hashkey;
414         cachepic_t *pic;
415         int flags;
416         fs_offset_t lmpsize;
417         unsigned char *lmpdata;
418         char lmpname[MAX_QPATH];
419
420         // check whether the picture has already been cached
421         crc = CRC_Block((unsigned char *)path, strlen(path));
422         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
423         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
424                 if (!strcmp (path, pic->name))
425                         return pic;
426
427         if (numcachepics == MAX_CACHED_PICS)
428         {
429                 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
430                 // FIXME: support NULL in callers?
431                 return cachepics; // return the first one
432         }
433         pic = cachepics + (numcachepics++);
434         strlcpy (pic->name, path, sizeof(pic->name));
435         // link into list
436         pic->chain = cachepichash[hashkey];
437         cachepichash[hashkey] = pic;
438
439         // check whether it is an dynamic texture (if so, we can directly use its texture handler)
440         pic->tex = CL_GetDynTexture( path );
441         // if so, set the width/height, too
442         if( pic->tex ) {
443                 pic->width = R_TextureWidth(pic->tex);
444                 pic->height = R_TextureHeight(pic->tex);
445                 // we're done now (early-out)
446                 return pic;
447         }
448
449         flags = TEXF_ALPHA;
450         if (persistent)
451                 flags |= TEXF_PRECACHE;
452         if (strcmp(path, "gfx/colorcontrol/ditherpattern"))
453                 flags |= TEXF_CLAMP;
454         if(allow_compression && gl_texturecompression_2d.integer)
455                 flags |= TEXF_COMPRESS;
456
457         // load a high quality image from disk if possible
458         pic->tex = loadtextureimage(drawtexturepool, path, false, flags, true);
459         if (pic->tex == NULL && !strncmp(path, "gfx/", 4))
460         {
461                 // compatibility with older versions which did not require gfx/ prefix
462                 pic->tex = loadtextureimage(drawtexturepool, path + 4, false, flags, true);
463         }
464         // if a high quality image was loaded, set the pic's size to match it, just
465         // in case there's no low quality version to get the size from
466         if (pic->tex)
467         {
468                 pic->width = R_TextureWidth(pic->tex);
469                 pic->height = R_TextureHeight(pic->tex);
470         }
471
472         // now read the low quality version (wad or lmp file), and take the pic
473         // size from that even if we don't upload the texture, this way the pics
474         // show up the right size in the menu even if they were replaced with
475         // higher or lower resolution versions
476         dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
477         if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
478         {
479                 if (lmpsize >= 9)
480                 {
481                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
482                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
483                         // if no high quality replacement image was found, upload the original low quality texture
484                         if (!pic->tex)
485                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
486                 }
487                 Mem_Free(lmpdata);
488         }
489         else if ((lmpdata = W_GetLumpName (path + 4)))
490         {
491                 if (!strcmp(path, "gfx/conchars"))
492                 {
493                         // conchars is a raw image and with color 0 as transparent instead of 255
494                         pic->width = 128;
495                         pic->height = 128;
496                         // if no high quality replacement image was found, upload the original low quality texture
497                         if (!pic->tex)
498                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_font);
499                 }
500                 else
501                 {
502                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
503                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
504                         // if no high quality replacement image was found, upload the original low quality texture
505                         if (!pic->tex)
506                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
507                 }
508         }
509
510         // if it's not found on disk, generate an image
511         if (pic->tex == NULL)
512         {
513                 pic->tex = draw_generatepic(path);
514                 pic->width = R_TextureWidth(pic->tex);
515                 pic->height = R_TextureHeight(pic->tex);
516         }
517
518         return pic;
519 }
520 cachepic_t      *Draw_CachePic (const char *path, qboolean persistent)
521 {
522         return Draw_CachePic_Compression(path, persistent, true);
523 }
524
525 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
526 {
527         int crc, hashkey;
528         cachepic_t *pic;
529
530         crc = CRC_Block((unsigned char *)picname, strlen(picname));
531         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
532         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
533                 if (!strcmp (picname, pic->name))
534                         break;
535
536         if (pic)
537         {
538                 if (pic->tex && pic->width == width && pic->height == height)
539                 {
540                         R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
541                         return pic;
542                 }
543         }
544         else
545         {
546                 if (pic == NULL)
547                 {
548                         if (numcachepics == MAX_CACHED_PICS)
549                         {
550                                 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
551                                 // FIXME: support NULL in callers?
552                                 return cachepics; // return the first one
553                         }
554                         pic = cachepics + (numcachepics++);
555                         strlcpy (pic->name, picname, sizeof(pic->name));
556                         // link into list
557                         pic->chain = cachepichash[hashkey];
558                         cachepichash[hashkey] = pic;
559                 }
560         }
561
562         pic->width = width;
563         pic->height = height;
564         if (pic->tex)
565                 R_FreeTexture(pic->tex);
566         pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, alpha ? TEXF_ALPHA : 0, NULL);
567         return pic;
568 }
569
570 void Draw_FreePic(const char *picname)
571 {
572         int crc;
573         int hashkey;
574         cachepic_t *pic;
575         // this doesn't really free the pic, but does free it's texture
576         crc = CRC_Block((unsigned char *)picname, strlen(picname));
577         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
578         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
579         {
580                 if (!strcmp (picname, pic->name) && pic->tex)
581                 {
582                         R_FreeTexture(pic->tex);
583                         pic->width = 0;
584                         pic->height = 0;
585                         return;
586                 }
587         }
588 }
589
590 extern int con_linewidth; // to force rewrapping
591 static void LoadFont(qboolean override, const char *name, dp_font_t *fnt)
592 {
593         int i;
594         float maxwidth;
595         char widthfile[MAX_QPATH];
596         char *widthbuf;
597         fs_offset_t widthbufsize;
598
599         if(override || !fnt->texpath[0])
600                 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
601
602         if(drawtexturepool == NULL)
603                 return; // before gl_draw_start, so will be loaded later
604
605         fnt->tex = Draw_CachePic_Compression(fnt->texpath, true, false)->tex;
606         if(fnt->tex == r_texture_notexture)
607         {
608                 fnt->tex = Draw_CachePic_Compression("gfx/conchars", true, false)->tex;
609                 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
610         }
611         else
612                 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
613
614         // unspecified width == 1 (base width)
615         for(i = 1; i < 256; ++i)
616                 fnt->width_of[i] = 1;
617
618         // FIXME load "name.width", if it fails, fill all with 1
619         if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
620         {
621                 float extraspacing = 0;
622                 const char *p = widthbuf;
623                 int ch = 0;
624
625                 while(ch < 256)
626                 {
627                         if(!COM_ParseToken_Simple(&p, false, false))
628                                 return;
629
630                         if(!strcmp(com_token, "extraspacing"))
631                         {
632                                 if(!COM_ParseToken_Simple(&p, false, false))
633                                         return;
634                                 extraspacing = atof(com_token);
635                         }
636                         else
637                                 fnt->width_of[ch++] = atof(com_token) + extraspacing;
638                 }
639
640                 Mem_Free(widthbuf);
641         }
642
643         maxwidth = fnt->width_of[1];
644         for(i = 2; i < 256; ++i)
645                 maxwidth = max(maxwidth, fnt->width_of[i]);
646         fnt->width_of[0] = maxwidth;
647
648         if(fnt == FONT_CONSOLE)
649                 con_linewidth = -1; // rewrap console in next frame
650 }
651
652 static dp_font_t *FindFont(const char *title)
653 {
654         int i;
655         for(i = 0; i < MAX_FONTS; ++i)
656                 if(!strcmp(dp_fonts[i].title, title))
657                         return &dp_fonts[i];
658         return NULL;
659 }
660
661 static void LoadFont_f(void)
662 {
663         dp_font_t *f;
664         int i;
665         if(Cmd_Argc() < 2)
666         {
667                 Con_Printf("Available font commands:\n");
668                 for(i = 0; i < MAX_FONTS; ++i)
669                         Con_Printf("  loadfont %s gfx/tgafile\n", dp_fonts[i].title);
670                 return;
671         }
672         f = FindFont(Cmd_Argv(1));
673         if(f == NULL)
674         {
675                 Con_Printf("font function not found\n");
676                 return;
677         }
678         LoadFont(true, (Cmd_Argc() < 3) ? "gfx/conchars" : Cmd_Argv(2), f);
679 }
680
681 /*
682 ===============
683 Draw_Init
684 ===============
685 */
686 static void gl_draw_start(void)
687 {
688         int i;
689         drawtexturepool = R_AllocTexturePool();
690
691         numcachepics = 0;
692         memset(cachepichash, 0, sizeof(cachepichash));
693
694         for(i = 0; i < MAX_FONTS; ++i)
695                 LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i]);
696
697         // draw the loading screen so people have something to see in the newly opened window
698         SCR_UpdateLoadingScreen(true);
699 }
700
701 static void gl_draw_shutdown(void)
702 {
703         R_FreeTexturePool(&drawtexturepool);
704
705         numcachepics = 0;
706         memset(cachepichash, 0, sizeof(cachepichash));
707 }
708
709 static void gl_draw_newmap(void)
710 {
711 }
712
713 void GL_Draw_Init (void)
714 {
715         int i, j;
716         Cvar_RegisterVariable(&r_textshadow);
717         Cvar_RegisterVariable(&r_textbrightness);
718         Cvar_RegisterVariable(&r_textcontrast);
719         Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
720         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
721
722         strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
723                 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
724         strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
725         strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
726         strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
727         strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
728         strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
729         strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
730         strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
731         for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
732                 if(!FONT_USER[i].title[0])
733                         dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++);
734 }
735
736 void _DrawQ_Setup(void)
737 {
738         if (r_refdef.draw2dstage)
739                 return;
740         r_refdef.draw2dstage = true;
741         CHECKGLERROR
742         qglViewport(r_refdef.view.x, vid.height - (r_refdef.view.y + r_refdef.view.height), r_refdef.view.width, r_refdef.view.height);CHECKGLERROR
743         GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
744         GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
745         qglDepthFunc(GL_LEQUAL);CHECKGLERROR
746         qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
747         GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
748         R_Mesh_Matrix(&identitymatrix);
749
750         GL_DepthMask(true);
751         GL_DepthRange(0, 1);
752         GL_PolygonOffset(0, 0);
753         GL_DepthTest(false);
754         GL_Color(1,1,1,1);
755         GL_AlphaTest(false);
756         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
757
758         if (gl_support_fragment_shader)
759         {
760                 qglUseProgramObjectARB(0);CHECKGLERROR
761         }
762 }
763
764 static void _DrawQ_ProcessDrawFlag(int flags)
765 {
766         _DrawQ_Setup();
767         CHECKGLERROR
768         if(flags == DRAWFLAG_ADDITIVE)
769                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
770         else if(flags == DRAWFLAG_MODULATE)
771                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
772         else if(flags == DRAWFLAG_2XMODULATE)
773                 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
774         else
775                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
776 }
777
778 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
779 {
780         float floats[20];
781
782         _DrawQ_ProcessDrawFlag(flags);
783         GL_Color(red, green, blue, alpha);
784
785         R_Mesh_VertexPointer(floats, 0, 0);
786         R_Mesh_ColorPointer(NULL, 0, 0);
787         R_Mesh_ResetTextureState();
788         if (pic)
789         {
790                 if (width == 0)
791                         width = pic->width;
792                 if (height == 0)
793                         height = pic->height;
794                 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
795                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
796
797 #if 1
798                 floats[12] = 0.0f;floats[13] = 0.0f;
799                 floats[14] = 1.0f;floats[15] = 0.0f;
800                 floats[16] = 1.0f;floats[17] = 1.0f;
801                 floats[18] = 0.0f;floats[19] = 1.0f;
802 #else
803       // AK07: lets be texel correct on the corners
804       {
805          float horz_offset = 0.5f / pic->width;
806          float vert_offset = 0.5f / pic->height;
807
808                    floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
809                    floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
810                    floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
811                    floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
812       }
813 #endif
814         }
815
816         floats[2] = floats[5] = floats[8] = floats[11] = 0;
817         floats[0] = floats[9] = x;
818         floats[1] = floats[4] = y;
819         floats[3] = floats[6] = x + width;
820         floats[7] = floats[10] = y + height;
821
822         R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
823 }
824
825 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
826 {
827         float floats[12];
828
829         _DrawQ_ProcessDrawFlag(flags);
830         GL_Color(red, green, blue, alpha);
831
832         R_Mesh_VertexPointer(floats, 0, 0);
833         R_Mesh_ColorPointer(NULL, 0, 0);
834         R_Mesh_ResetTextureState();
835
836         floats[2] = floats[5] = floats[8] = floats[11] = 0;
837         floats[0] = floats[9] = x;
838         floats[1] = floats[4] = y;
839         floats[3] = floats[6] = x + width;
840         floats[7] = floats[10] = y + height;
841
842         R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
843 }
844
845 // color tag printing
846 static const vec4_t string_colors[] =
847 {
848         // Quake3 colors
849         // LordHavoc: why on earth is cyan before magenta in Quake3?
850         // LordHavoc: note: Doom3 uses white for [0] and [7]
851         {0.0, 0.0, 0.0, 1.0}, // black
852         {1.0, 0.0, 0.0, 1.0}, // red
853         {0.0, 1.0, 0.0, 1.0}, // green
854         {1.0, 1.0, 0.0, 1.0}, // yellow
855         {0.0, 0.0, 1.0, 1.0}, // blue
856         {0.0, 1.0, 1.0, 1.0}, // cyan
857         {1.0, 0.0, 1.0, 1.0}, // magenta
858         {1.0, 1.0, 1.0, 1.0}, // white
859         // [515]'s BX_COLOREDTEXT extension
860         {1.0, 1.0, 1.0, 0.5}, // half transparent
861         {0.5, 0.5, 0.5, 1.0}  // half brightness
862         // Black's color table
863         //{1.0, 1.0, 1.0, 1.0},
864         //{1.0, 0.0, 0.0, 1.0},
865         //{0.0, 1.0, 0.0, 1.0},
866         //{0.0, 0.0, 1.0, 1.0},
867         //{1.0, 1.0, 0.0, 1.0},
868         //{0.0, 1.0, 1.0, 1.0},
869         //{1.0, 0.0, 1.0, 1.0},
870         //{0.1, 0.1, 0.1, 1.0}
871 };
872
873 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
874
875 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
876 {
877         float C = r_textcontrast.value;
878         float B = r_textbrightness.value;
879         Vector4Copy(string_colors[colorindex], color);
880         Vector4Set(color, (color[0] * C + B) * r, (color[1] * C + B) * g, (color[2] * C + B) * b, color[3] * a);
881         if (shadow)
882         {
883                 float shadowalpha = color[0]+color[1]+color[2] * 0.8;
884                 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
885         }
886 }
887
888 float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
889 {
890         int num, colorindex = STRING_COLOR_DEFAULT;
891         size_t i;
892         float x = 0;
893
894         if (*maxlen < 1)
895                 *maxlen = 1<<30;
896
897         if (!outcolor || *outcolor == -1)
898                 colorindex = STRING_COLOR_DEFAULT;
899         else
900                 colorindex = *outcolor;
901
902         for (i = 0;i < *maxlen && text[i];i++)
903         {
904                 if (text[i] == ' ')
905                 {
906                         if(x + fnt->width_of[' '] > maxwidth)
907                                 break; // oops, can't draw this
908                         x += fnt->width_of[' '];
909                         continue;
910                 }
911                 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen)
912                 {
913                         if (text[i+1] == STRING_COLOR_TAG)
914                         {
915                                 i++;
916                         }
917                         else if (text[i+1] >= '0' && text[i+1] <= '9')
918                         {
919                                 colorindex = text[i+1] - '0';
920                                 i++;
921                                 continue;
922                         }
923                 }
924                 num = (unsigned char) text[i];
925                 if(x + fnt->width_of[num] > maxwidth)
926                         break; // oops, can't draw this
927                 x += fnt->width_of[num];
928         }
929
930         *maxlen = i;
931
932         if (outcolor)
933                 *outcolor = colorindex;
934
935         return x;
936 }
937
938 float DrawQ_String_Font(float startx, float starty, const char *text, size_t maxlen, float w, float h, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt)
939 {
940         int num, shadow, colorindex = STRING_COLOR_DEFAULT;
941         size_t i;
942         float x = startx, y, s, t, u, v;
943         float *av, *at, *ac;
944         float color[4];
945         int batchcount;
946         float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
947         float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
948         float color4f[QUADELEMENTS_MAXQUADS*4*4];
949
950         if (maxlen < 1)
951                 maxlen = 1<<30;
952
953         _DrawQ_ProcessDrawFlag(flags);
954
955         R_Mesh_ColorPointer(color4f, 0, 0);
956         R_Mesh_ResetTextureState();
957         R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
958         R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
959         R_Mesh_VertexPointer(vertex3f, 0, 0);
960
961         ac = color4f;
962         at = texcoord2f;
963         av = vertex3f;
964         batchcount = 0;
965
966         for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
967         {
968                 if (!outcolor || *outcolor == -1)
969                         colorindex = STRING_COLOR_DEFAULT;
970                 else
971                         colorindex = *outcolor;
972                 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
973
974                 x = startx;
975                 y = starty;
976                 if (shadow)
977                 {
978                         x += r_textshadow.value;
979                         y += r_textshadow.value;
980                 }
981                 for (i = 0;i < maxlen && text[i];i++)
982                 {
983                         if (text[i] == ' ')
984                         {
985                                 x += fnt->width_of[' '] * w;
986                                 continue;
987                         }
988                         if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen)
989                         {
990                                 if (text[i+1] == STRING_COLOR_TAG)
991                                 {
992                                         i++;
993                                 }
994                                 else if (text[i+1] >= '0' && text[i+1] <= '9')
995                                 {
996                                         colorindex = text[i+1] - '0';
997                                         DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
998                                         i++;
999                                         continue;
1000                                 }
1001                         }
1002                         num = (unsigned char) text[i];
1003                         // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1004                         s = (num & 15)*0.0625f + (0.5f / 256.0f);
1005                         t = (num >> 4)*0.0625f + (0.5f / 256.0f);
1006                         u = 0.0625f - (1.0f / 256.0f);
1007                         v = 0.0625f - (1.0f / 256.0f);
1008                         ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1009                         ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1010                         ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1011                         ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1012                         at[ 0] = s  ;at[ 1] = t  ;
1013                         at[ 2] = s+u;at[ 3] = t  ;
1014                         at[ 4] = s+u;at[ 5] = t+v;
1015                         at[ 6] = s  ;at[ 7] = t+v;
1016                         av[ 0] = x  ;av[ 1] = y  ;av[ 2] = 10;
1017                         av[ 3] = x+w;av[ 4] = y  ;av[ 5] = 10;
1018                         av[ 6] = x+w;av[ 7] = y+h;av[ 8] = 10;
1019                         av[ 9] = x  ;av[10] = y+h;av[11] = 10;
1020                         ac += 16;
1021                         at += 8;
1022                         av += 12;
1023                         batchcount++;
1024                         if (batchcount >= QUADELEMENTS_MAXQUADS)
1025                         {
1026                                 GL_LockArrays(0, batchcount * 4);
1027                                 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
1028                                 GL_LockArrays(0, 0);
1029                                 batchcount = 0;
1030                                 ac = color4f;
1031                                 at = texcoord2f;
1032                                 av = vertex3f;
1033                         }
1034                         x += fnt->width_of[num] * w;
1035                 }
1036         }
1037         if (batchcount > 0)
1038         {
1039                 GL_LockArrays(0, batchcount * 4);
1040                 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
1041                 GL_LockArrays(0, 0);
1042         }
1043
1044         if (outcolor)
1045                 *outcolor = colorindex;
1046
1047         // note: this relies on the proper text (not shadow) being drawn last
1048         return x;
1049 }
1050
1051 float DrawQ_String(float startx, float starty, const char *text, size_t maxlen, float w, float h, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor, qboolean ignorecolorcodes)
1052 {
1053         return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]);
1054 }
1055
1056 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1057 {
1058         return DrawQ_TextWidth_Font_UntilWidth_TrackColors(text, maxlen, NULL, ignorecolorcodes, fnt, maxWidth);
1059 }
1060
1061 float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt)
1062 {
1063         return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, ignorecolorcodes, fnt, 1000000000);
1064 }
1065
1066 #if 0
1067 // not used
1068 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1069 {
1070         int color, numchars = 0;
1071         char *outputend2c = output2c + maxoutchars - 2;
1072         if (!outcolor || *outcolor == -1)
1073                 color = STRING_COLOR_DEFAULT;
1074         else
1075                 color = *outcolor;
1076         if (!maxreadchars)
1077                 maxreadchars = 1<<30;
1078         textend = text + maxreadchars;
1079         while (text != textend && *text)
1080         {
1081                 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1082                 {
1083                         if (text[1] == STRING_COLOR_TAG)
1084                                 text++;
1085                         else if (text[1] >= '0' && text[1] <= '9')
1086                         {
1087                                 color = text[1] - '0';
1088                                 text += 2;
1089                                 continue;
1090                         }
1091                 }
1092                 if (output2c >= outputend2c)
1093                         break;
1094                 *output2c++ = *text++;
1095                 *output2c++ = color;
1096                 numchars++;
1097         }
1098         output2c[0] = output2c[1] = 0;
1099         if (outcolor)
1100                 *outcolor = color;
1101         return numchars;
1102 }
1103 #endif
1104
1105 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)
1106 {
1107         float floats[36];
1108
1109         _DrawQ_ProcessDrawFlag(flags);
1110
1111         R_Mesh_VertexPointer(floats, 0, 0);
1112         R_Mesh_ColorPointer(floats + 20, 0, 0);
1113         R_Mesh_ResetTextureState();
1114         if (pic)
1115         {
1116                 if (width == 0)
1117                         width = pic->width;
1118                 if (height == 0)
1119                         height = pic->height;
1120                 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1121                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1122                 floats[12] = s1;floats[13] = t1;
1123                 floats[14] = s2;floats[15] = t2;
1124                 floats[16] = s4;floats[17] = t4;
1125                 floats[18] = s3;floats[19] = t3;
1126         }
1127
1128         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1129         floats[0] = floats[9] = x;
1130         floats[1] = floats[4] = y;
1131         floats[3] = floats[6] = x + width;
1132         floats[7] = floats[10] = y + height;
1133         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1134         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1135         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1136         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1137
1138         R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
1139 }
1140
1141 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1142 {
1143         _DrawQ_ProcessDrawFlag(flags);
1144
1145         R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1146         R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1147         R_Mesh_ResetTextureState();
1148         R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
1149         R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1150
1151         GL_LockArrays(0, mesh->num_vertices);
1152         R_Mesh_Draw(0, mesh->num_vertices, mesh->num_triangles, mesh->data_element3i, 0, 0);
1153         GL_LockArrays(0, 0);
1154 }
1155
1156 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1157 {
1158         int num;
1159
1160         _DrawQ_ProcessDrawFlag(flags);
1161
1162         GL_Color(1,1,1,1);
1163         CHECKGLERROR
1164         qglBegin(GL_LINE_LOOP);
1165         for (num = 0;num < mesh->num_vertices;num++)
1166         {
1167                 if (mesh->data_color4f)
1168                         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]);
1169                 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1170         }
1171         qglEnd();
1172         CHECKGLERROR
1173 }
1174
1175 //[515]: this is old, delete
1176 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1177 {
1178         _DrawQ_ProcessDrawFlag(flags);
1179
1180         CHECKGLERROR
1181         qglLineWidth(width);CHECKGLERROR
1182
1183         GL_Color(r,g,b,alpha);
1184         CHECKGLERROR
1185         qglBegin(GL_LINES);
1186         qglVertex2f(x1, y1);
1187         qglVertex2f(x2, y2);
1188         qglEnd();
1189         CHECKGLERROR
1190 }
1191
1192 void DrawQ_SetClipArea(float x, float y, float width, float height)
1193 {
1194         _DrawQ_Setup();
1195
1196         // We have to convert the con coords into real coords
1197         // OGL uses top to bottom
1198         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)));
1199
1200         GL_ScissorTest(true);
1201 }
1202
1203 void DrawQ_ResetClipArea(void)
1204 {
1205         _DrawQ_Setup();
1206         GL_ScissorTest(false);
1207 }
1208
1209 void DrawQ_Finish(void)
1210 {
1211         r_refdef.draw2dstage = false;
1212 }
1213
1214 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1215 void R_DrawGamma(void)
1216 {
1217         float c[4];
1218         if (!vid_usinghwgamma)
1219         {
1220                 // all the blends ignore depth
1221                 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1222                 R_Mesh_ColorPointer(NULL, 0, 0);
1223                 R_Mesh_ResetTextureState();
1224                 GL_DepthMask(true);
1225                 GL_DepthRange(0, 1);
1226                 GL_PolygonOffset(0, 0);
1227                 GL_DepthTest(false);
1228                 if (v_color_enable.integer)
1229                 {
1230                         c[0] = v_color_white_r.value;
1231                         c[1] = v_color_white_g.value;
1232                         c[2] = v_color_white_b.value;
1233                 }
1234                 else
1235                         c[0] = c[1] = c[2] = v_contrast.value;
1236                 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1237                 {
1238                         GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1239                         while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1240                         {
1241                                 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1242                                 R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);
1243                                 VectorScale(c, 0.5, c);
1244                         }
1245                 }
1246                 if (v_color_enable.integer)
1247                 {
1248                         c[0] = v_color_black_r.value;
1249                         c[1] = v_color_black_g.value;
1250                         c[2] = v_color_black_b.value;
1251                 }
1252                 else
1253                         c[0] = c[1] = c[2] = v_brightness.value;
1254                 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1255                 {
1256                         GL_BlendFunc(GL_ONE, GL_ONE);
1257                         GL_Color(c[0], c[1], c[2], 1);
1258                         R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);
1259                 }
1260         }
1261 }
1262