]> icculus.org git repositories - divverent/darkplaces.git/blob - gl_draw.c
the light has been defeated by the shadow (r_light moved into r_shadow)
[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 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, NULL);
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 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         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_FONTS; ++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       // AK07: lets be texel correct on the corners
796       {
797          float horz_offset = 0.5f / pic->width;
798          float vert_offset = 0.5f / pic->height;
799
800                    floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
801                    floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
802                    floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
803                    floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
804       }
805         }
806
807         floats[2] = floats[5] = floats[8] = floats[11] = 0;
808         floats[0] = floats[9] = x;
809         floats[1] = floats[4] = y;
810         floats[3] = floats[6] = x + width;
811         floats[7] = floats[10] = y + height;
812
813         R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
814 }
815
816 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
817 {
818         float floats[12];
819
820         _DrawQ_ProcessDrawFlag(flags);
821         GL_Color(red, green, blue, alpha);
822
823         R_Mesh_VertexPointer(floats, 0, 0);
824         R_Mesh_ColorPointer(NULL, 0, 0);
825         R_Mesh_ResetTextureState();
826
827         floats[2] = floats[5] = floats[8] = floats[11] = 0;
828         floats[0] = floats[9] = x;
829         floats[1] = floats[4] = y;
830         floats[3] = floats[6] = x + width;
831         floats[7] = floats[10] = y + height;
832
833         R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
834 }
835
836 // color tag printing
837 static vec4_t string_colors[] =
838 {
839         // Quake3 colors
840         // LordHavoc: why on earth is cyan before magenta in Quake3?
841         // LordHavoc: note: Doom3 uses white for [0] and [7]
842         {0.0, 0.0, 0.0, 1.0}, // black
843         {1.0, 0.0, 0.0, 1.0}, // red
844         {0.0, 1.0, 0.0, 1.0}, // green
845         {1.0, 1.0, 0.0, 1.0}, // yellow
846         {0.0, 0.0, 1.0, 1.0}, // blue
847         {0.0, 1.0, 1.0, 1.0}, // cyan
848         {1.0, 0.0, 1.0, 1.0}, // magenta
849         {1.0, 1.0, 1.0, 1.0}, // white
850         // [515]'s BX_COLOREDTEXT extension
851         {1.0, 1.0, 1.0, 0.5}, // half transparent
852         {0.5, 0.5, 0.5, 1.0}  // half brightness
853         // Black's color table
854         //{1.0, 1.0, 1.0, 1.0},
855         //{1.0, 0.0, 0.0, 1.0},
856         //{0.0, 1.0, 0.0, 1.0},
857         //{0.0, 0.0, 1.0, 1.0},
858         //{1.0, 1.0, 0.0, 1.0},
859         //{0.0, 1.0, 1.0, 1.0},
860         //{1.0, 0.0, 1.0, 1.0},
861         //{0.1, 0.1, 0.1, 1.0}
862 };
863
864 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
865
866 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
867 {
868         float v = r_textbrightness.value;
869         Vector4Copy(string_colors[colorindex], color);
870         Vector4Set(color, (color[0] * (1-v) + v) * r, (color[1] * (1-v) + v) * g, (color[2] * (1-v) + v) * b, color[3] * a);
871         if (shadow)
872         {
873                 float shadowalpha = color[0]+color[1]+color[2] * 0.8;
874                 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
875         }
876 }
877
878 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)
879 {
880         int num, shadow, colorindex = STRING_COLOR_DEFAULT;
881         size_t i;
882         float x = startx, y, s, t, u, v;
883         float *av, *at, *ac;
884         float color[4];
885         int batchcount;
886         float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
887         float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
888         float color4f[QUADELEMENTS_MAXQUADS*4*4];
889         qboolean checkwidth;
890
891         if (*maxlen < 1)
892                 *maxlen = 1<<30;
893
894         // when basealpha == 0, skip as much as possible (just return width)
895         if(basealpha > 0)
896         {
897                 _DrawQ_ProcessDrawFlag(flags);
898
899                 R_Mesh_ColorPointer(color4f, 0, 0);
900                 R_Mesh_ResetTextureState();
901                 R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
902                 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
903                 R_Mesh_VertexPointer(vertex3f, 0, 0);
904         }
905
906         ac = color4f;
907         at = texcoord2f;
908         av = vertex3f;
909         batchcount = 0;
910         checkwidth = (maxx >= startx);
911
912         for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
913         {
914                 if (!outcolor || *outcolor == -1)
915                         colorindex = STRING_COLOR_DEFAULT;
916                 else
917                         colorindex = *outcolor;
918                 if(basealpha > 0)
919                         DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
920
921                 x = startx;
922                 y = starty;
923                 if (shadow)
924                 {
925                         x += r_textshadow.value;
926                         y += r_textshadow.value;
927                 }
928                 for (i = 0;i < *maxlen && text[i];i++)
929                 {
930                         if (text[i] == ' ')
931                         {
932                                 if(checkwidth)
933                                         if(x + fnt->width_of[' '] * w > maxx)
934                                                 break; // oops, can't draw this
935                                 x += fnt->width_of[' '] * w;
936                                 continue;
937                         }
938                         if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen)
939                         {
940                                 if (text[i+1] == STRING_COLOR_TAG)
941                                 {
942                                         i++;
943                                 }
944                                 else if (text[i+1] >= '0' && text[i+1] <= '9')
945                                 {
946                                         colorindex = text[i+1] - '0';
947                                         DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
948                                         i++;
949                                         continue;
950                                 }
951                         }
952                         num = (unsigned char) text[i];
953                         if(checkwidth)
954                                 if(x + fnt->width_of[num] * w > maxx)
955                                         break; // oops, can't draw this
956                         if(basealpha > 0)
957                         {
958                                 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
959                                 s = (num & 15)*0.0625f + (0.5f / 256.0f);
960                                 t = (num >> 4)*0.0625f + (0.5f / 256.0f);
961                                 u = 0.0625f - (1.0f / 256.0f);
962                                 v = 0.0625f - (1.0f / 256.0f);
963                                 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
964                                 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
965                                 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
966                                 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
967                                 at[ 0] = s  ;at[ 1] = t  ;
968                                 at[ 2] = s+u;at[ 3] = t  ;
969                                 at[ 4] = s+u;at[ 5] = t+v;
970                                 at[ 6] = s  ;at[ 7] = t+v;
971                                 av[ 0] = x  ;av[ 1] = y  ;av[ 2] = 10;
972                                 av[ 3] = x+w;av[ 4] = y  ;av[ 5] = 10;
973                                 av[ 6] = x+w;av[ 7] = y+h;av[ 8] = 10;
974                                 av[ 9] = x  ;av[10] = y+h;av[11] = 10;
975                                 ac += 16;
976                                 at += 8;
977                                 av += 12;
978                                 batchcount++;
979                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
980                                 {
981                                         if (basealpha >= (1.0f / 255.0f))
982                                         {
983                                                 GL_LockArrays(0, batchcount * 4);
984                                                 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
985                                                 GL_LockArrays(0, 0);
986                                         }
987                                         batchcount = 0;
988                                         ac = color4f;
989                                         at = texcoord2f;
990                                         av = vertex3f;
991                                 }
992                         }
993                         x += fnt->width_of[num] * w;
994                 }
995                 if(checkwidth)
996                         *maxlen = i;
997                 checkwidth = 0; // we've done all we had to
998         }
999         if (basealpha > 0)
1000         {
1001                 if (batchcount > 0)
1002                 {
1003                         GL_LockArrays(0, batchcount * 4);
1004                         R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
1005                         GL_LockArrays(0, 0);
1006                 }
1007         }
1008
1009         if (outcolor)
1010                 *outcolor = colorindex;
1011
1012         // note: this relies on the proper text (not shadow) being drawn last
1013         return x;
1014 }
1015
1016 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)
1017 {
1018         return DrawQ_String_Font_UntilX(startx, starty, text, &maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt, startx-1);
1019 }
1020
1021 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)
1022 {
1023         return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]);
1024 }
1025
1026 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, float scalex, float scaley, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1027 {
1028         return DrawQ_String_Font_UntilX(0, 0, text, maxlen, scalex, scaley, 1, 1, 1, 0, 0, NULL, ignorecolorcodes, fnt, maxWidth);
1029 }
1030
1031 float DrawQ_TextWidth_Font(const char *text, size_t maxlen, float scalex, float scaley, qboolean ignorecolorcodes, const dp_font_t *fnt)
1032 {
1033         return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, scalex, scaley, ignorecolorcodes, fnt, -1);
1034 }
1035
1036 #if 0
1037 // not used
1038 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1039 {
1040         int color, numchars = 0;
1041         char *outputend2c = output2c + maxoutchars - 2;
1042         if (!outcolor || *outcolor == -1)
1043                 color = STRING_COLOR_DEFAULT;
1044         else
1045                 color = *outcolor;
1046         if (!maxreadchars)
1047                 maxreadchars = 1<<30;
1048         textend = text + maxreadchars;
1049         while (text != textend && *text)
1050         {
1051                 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1052                 {
1053                         if (text[1] == STRING_COLOR_TAG)
1054                                 text++;
1055                         else if (text[1] >= '0' && text[1] <= '9')
1056                         {
1057                                 color = text[1] - '0';
1058                                 text += 2;
1059                                 continue;
1060                         }
1061                 }
1062                 if (output2c >= outputend2c)
1063                         break;
1064                 *output2c++ = *text++;
1065                 *output2c++ = color;
1066                 numchars++;
1067         }
1068         output2c[0] = output2c[1] = 0;
1069         if (outcolor)
1070                 *outcolor = color;
1071         return numchars;
1072 }
1073 #endif
1074
1075 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)
1076 {
1077         float floats[36];
1078
1079         _DrawQ_ProcessDrawFlag(flags);
1080
1081         R_Mesh_VertexPointer(floats, 0, 0);
1082         R_Mesh_ColorPointer(floats + 20, 0, 0);
1083         R_Mesh_ResetTextureState();
1084         if (pic)
1085         {
1086                 if (width == 0)
1087                         width = pic->width;
1088                 if (height == 0)
1089                         height = pic->height;
1090                 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1091                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1092                 floats[12] = s1;floats[13] = t1;
1093                 floats[14] = s2;floats[15] = t2;
1094                 floats[16] = s4;floats[17] = t4;
1095                 floats[18] = s3;floats[19] = t3;
1096         }
1097
1098         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1099         floats[0] = floats[9] = x;
1100         floats[1] = floats[4] = y;
1101         floats[3] = floats[6] = x + width;
1102         floats[7] = floats[10] = y + height;
1103         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1104         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1105         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1106         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1107
1108         R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
1109 }
1110
1111 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1112 {
1113         _DrawQ_ProcessDrawFlag(flags);
1114
1115         R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1116         R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1117         R_Mesh_ResetTextureState();
1118         R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
1119         R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1120
1121         GL_LockArrays(0, mesh->num_vertices);
1122         R_Mesh_Draw(0, mesh->num_vertices, mesh->num_triangles, mesh->data_element3i, 0, 0);
1123         GL_LockArrays(0, 0);
1124 }
1125
1126 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1127 {
1128         int num;
1129
1130         _DrawQ_ProcessDrawFlag(flags);
1131
1132         GL_Color(1,1,1,1);
1133         CHECKGLERROR
1134         qglBegin(GL_LINE_LOOP);
1135         for (num = 0;num < mesh->num_vertices;num++)
1136         {
1137                 if (mesh->data_color4f)
1138                         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]);
1139                 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1140         }
1141         qglEnd();
1142         CHECKGLERROR
1143 }
1144
1145 //[515]: this is old, delete
1146 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1147 {
1148         _DrawQ_ProcessDrawFlag(flags);
1149
1150         CHECKGLERROR
1151         qglLineWidth(width);CHECKGLERROR
1152
1153         GL_Color(r,g,b,alpha);
1154         CHECKGLERROR
1155         qglBegin(GL_LINES);
1156         qglVertex2f(x1, y1);
1157         qglVertex2f(x2, y2);
1158         qglEnd();
1159         CHECKGLERROR
1160 }
1161
1162 void DrawQ_SetClipArea(float x, float y, float width, float height)
1163 {
1164         _DrawQ_Setup();
1165
1166         // We have to convert the con coords into real coords
1167         // OGL uses top to bottom
1168         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)));
1169
1170         GL_ScissorTest(true);
1171 }
1172
1173 void DrawQ_ResetClipArea(void)
1174 {
1175         _DrawQ_Setup();
1176         GL_ScissorTest(false);
1177 }
1178
1179 void DrawQ_Finish(void)
1180 {
1181         r_refdef.draw2dstage = false;
1182 }
1183
1184 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1185 void R_DrawGamma(void)
1186 {
1187         float c[4];
1188         if (!vid_usinghwgamma)
1189         {
1190                 // all the blends ignore depth
1191                 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1192                 R_Mesh_ColorPointer(NULL, 0, 0);
1193                 R_Mesh_ResetTextureState();
1194                 GL_DepthMask(true);
1195                 GL_DepthRange(0, 1);
1196                 GL_PolygonOffset(0, 0);
1197                 GL_DepthTest(false);
1198                 if (v_color_enable.integer)
1199                 {
1200                         c[0] = v_color_white_r.value;
1201                         c[1] = v_color_white_g.value;
1202                         c[2] = v_color_white_b.value;
1203                 }
1204                 else
1205                         c[0] = c[1] = c[2] = v_contrast.value;
1206                 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1207                 {
1208                         GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1209                         while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1210                         {
1211                                 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1212                                 R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);
1213                                 VectorScale(c, 0.5, c);
1214                         }
1215                 }
1216                 if (v_color_enable.integer)
1217                 {
1218                         c[0] = v_color_black_r.value;
1219                         c[1] = v_color_black_g.value;
1220                         c[2] = v_color_black_b.value;
1221                 }
1222                 else
1223                         c[0] = c[1] = c[2] = v_brightness.value;
1224                 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1225                 {
1226                         GL_BlendFunc(GL_ONE, GL_ONE);
1227                         GL_Color(c[0], c[1], c[2], 1);
1228                         R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);
1229                 }
1230         }
1231 }
1232