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