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