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