]> git.xonotic.org Git - xonotic/darkplaces.git/blob - gl_draw.c
refactoring to avoid doing the same mistake again (see HEAD^)
[xonotic/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 #include "ft2.h"
29 #include "ft2_fontdefs.h"
30
31 dp_fonts_t dp_fonts;
32 static mempool_t *fonts_mempool = NULL;
33
34 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)"};
35 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)"};
36 cvar_t r_textcontrast = {CVAR_SAVE, "r_textcontrast", "1", "additional contrast for text color codes (1 keeps colors as is, 0 makes them all black)"};
37
38 cvar_t r_font_postprocess_blur = {CVAR_SAVE, "r_font_postprocess_blur", "0", "font blur amount"};
39 cvar_t r_font_postprocess_outline = {CVAR_SAVE, "r_font_postprocess_outline", "0", "font outline amount"};
40 cvar_t r_font_postprocess_shadow_x = {CVAR_SAVE, "r_font_postprocess_shadow_x", "0", "font shadow X shift amount, applied during outlining"};
41 cvar_t r_font_postprocess_shadow_y = {CVAR_SAVE, "r_font_postprocess_shadow_y", "0", "font shadow Y shift amount, applied during outlining"};
42 cvar_t r_font_postprocess_shadow_z = {CVAR_SAVE, "r_font_postprocess_shadow_z", "0", "font shadow Z shift amount, applied during blurring"};
43 cvar_t r_font_hinting = {CVAR_SAVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"};
44 cvar_t r_font_antialias = {CVAR_SAVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */};
45
46 extern cvar_t v_glslgamma;
47
48 //=============================================================================
49 /* Support Routines */
50
51 #define FONT_FILESIZE 13468
52 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
53 static cachepic_t cachepics[MAX_CACHED_PICS];
54 static int numcachepics;
55
56 rtexturepool_t *drawtexturepool;
57
58 static const unsigned char concharimage[FONT_FILESIZE] =
59 {
60 #include "lhfont.h"
61 };
62
63 static rtexture_t *draw_generateconchars(void)
64 {
65         int i;
66         unsigned char *data;
67         double random;
68         rtexture_t *tex;
69
70         data = LoadTGA_BGRA (concharimage, FONT_FILESIZE, NULL);
71 // Gold numbers
72         for (i = 0;i < 8192;i++)
73         {
74                 random = lhrandom (0.0,1.0);
75                 data[i*4+3] = data[i*4+0];
76                 data[i*4+2] = 83 + (unsigned char)(random * 64);
77                 data[i*4+1] = 71 + (unsigned char)(random * 32);
78                 data[i*4+0] = 23 + (unsigned char)(random * 16);
79         }
80 // White chars
81         for (i = 8192;i < 32768;i++)
82         {
83                 random = lhrandom (0.0,1.0);
84                 data[i*4+3] = data[i*4+0];
85                 data[i*4+2] = 95 + (unsigned char)(random * 64);
86                 data[i*4+1] = 95 + (unsigned char)(random * 64);
87                 data[i*4+0] = 95 + (unsigned char)(random * 64);
88         }
89 // Gold numbers
90         for (i = 32768;i < 40960;i++)
91         {
92                 random = lhrandom (0.0,1.0);
93                 data[i*4+3] = data[i*4+0];
94                 data[i*4+2] = 83 + (unsigned char)(random * 64);
95                 data[i*4+1] = 71 + (unsigned char)(random * 32);
96                 data[i*4+0] = 23 + (unsigned char)(random * 16);
97         }
98 // Red chars
99         for (i = 40960;i < 65536;i++)
100         {
101                 random = lhrandom (0.0,1.0);
102                 data[i*4+3] = data[i*4+0];
103                 data[i*4+2] = 96 + (unsigned char)(random * 64);
104                 data[i*4+1] = 43 + (unsigned char)(random * 32);
105                 data[i*4+0] = 27 + (unsigned char)(random * 32);
106         }
107
108 #if 0
109         Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, data);
110 #endif
111
112         tex = R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, data, TEXTYPE_BGRA, TEXF_ALPHA, -1, NULL);
113         Mem_Free(data);
114         return tex;
115 }
116
117 static rtexture_t *draw_generateditherpattern(void)
118 {
119         int x, y;
120         unsigned char pixels[8][8];
121         for (y = 0;y < 8;y++)
122                 for (x = 0;x < 8;x++)
123                         pixels[y][x] = ((x^y) & 4) ? 254 : 0;
124         return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST, -1, palette_bgra_transparent);
125 }
126
127 typedef struct embeddedpic_s
128 {
129         const char *name;
130         int width;
131         int height;
132         const char *pixels;
133 }
134 embeddedpic_t;
135
136 static const embeddedpic_t embeddedpics[] =
137 {
138         {
139         "gfx/prydoncursor001", 16, 16,
140         "477777774......."
141         "77.....6........"
142         "7.....6........."
143         "7....6.........."
144         "7.....6........."
145         "7..6...6........"
146         "7.6.6...6......."
147         "76...6...6......"
148         "4.....6.6......."
149         ".......6........"
150         "................"
151         "................"
152         "................"
153         "................"
154         "................"
155         "................"
156         },
157         {
158         "ui/mousepointer", 16, 16,
159         "477777774......."
160         "77.....6........"
161         "7.....6........."
162         "7....6.........."
163         "7.....6........."
164         "7..6...6........"
165         "7.6.6...6......."
166         "76...6...6......"
167         "4.....6.6......."
168         ".......6........"
169         "................"
170         "................"
171         "................"
172         "................"
173         "................"
174         "................"
175         },
176         {
177         "gfx/crosshair1", 16, 16,
178         "................"
179         "................"
180         "................"
181         "...33......33..."
182         "...355....553..."
183         "....577..775...."
184         ".....77..77....."
185         "................"
186         "................"
187         ".....77..77....."
188         "....577..775...."
189         "...355....553..."
190         "...33......33..."
191         "................"
192         "................"
193         "................"
194         },
195         {
196         "gfx/crosshair2", 16, 16,
197         "................"
198         "................"
199         "................"
200         "...3........3..."
201         "....5......5...."
202         ".....7....7....."
203         "......7..7......"
204         "................"
205         "................"
206         "......7..7......"
207         ".....7....7....."
208         "....5......5...."
209         "...3........3..."
210         "................"
211         "................"
212         "................"
213         },
214         {
215         "gfx/crosshair3", 16, 16,
216         "................"
217         ".......77......."
218         ".......77......."
219         "................"
220         "................"
221         ".......44......."
222         ".......44......."
223         ".77..44..44..77."
224         ".77..44..44..77."
225         ".......44......."
226         ".......44......."
227         "................"
228         "................"
229         ".......77......."
230         ".......77......."
231         "................"
232         },
233         {
234         "gfx/crosshair4", 16, 16,
235         "................"
236         "................"
237         "................"
238         "................"
239         "................"
240         "................"
241         "................"
242         "................"
243         "........7777777."
244         "........752....."
245         "........72......"
246         "........7......."
247         "........7......."
248         "........7......."
249         "........7......."
250         "................"
251         },
252         {
253         "gfx/crosshair5", 8, 8,
254         "........"
255         "........"
256         "....7..."
257         "........"
258         "..7.7.7."
259         "........"
260         "....7..."
261         "........"
262         },
263         {
264         "gfx/crosshair6", 2, 2,
265         "77"
266         "77"
267         },
268         {
269         "gfx/crosshair7", 16, 16,
270         "................"
271         ".3............3."
272         "..5...2332...5.."
273         "...7.3....3.7..."
274         "....7......7...."
275         "...3.7....7.3..."
276         "..2...7..7...2.."
277         "..3..........3.."
278         "..3..........3.."
279         "..2...7..7...2.."
280         "...3.7....7.3..."
281         "....7......7...."
282         "...7.3....3.7..."
283         "..5...2332...5.."
284         ".3............3."
285         "................"
286         },
287         {NULL, 0, 0, NULL}
288 };
289
290 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
291 {
292         const embeddedpic_t *p;
293         for (p = embeddedpics;p->name;p++)
294                 if (!strcmp(name, p->name))
295                         return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA, -1, palette_bgra_embeddedpic);
296         if (!strcmp(name, "gfx/conchars"))
297                 return draw_generateconchars();
298         if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
299                 return draw_generateditherpattern();
300         if (!quiet)
301                 Con_DPrintf("Draw_CachePic: failed to load %s\n", name);
302         return r_texture_notexture;
303 }
304
305 int draw_frame = 1;
306
307 /*
308 ================
309 Draw_CachePic
310 ================
311 */
312 // FIXME: move this to client somehow
313 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
314 {
315         int crc, hashkey;
316         unsigned char *pixels = NULL;
317         cachepic_t *pic;
318         fs_offset_t lmpsize;
319         unsigned char *lmpdata;
320         char lmpname[MAX_QPATH];
321         int texflags;
322         int j;
323         qboolean ddshasalpha;
324         float ddsavgcolor[4];
325         qboolean loaded = false;
326
327         texflags = TEXF_ALPHA;
328         if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
329                 texflags |= TEXF_CLAMP;
330         if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer && gl_texturecompression.integer)
331                 texflags |= TEXF_COMPRESS;
332
333         // check whether the picture has already been cached
334         crc = CRC_Block((unsigned char *)path, strlen(path));
335         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
336         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
337         {
338                 if (!strcmp (path, pic->name))
339                 {
340                         // if it was created (or replaced) by Draw_NewPic, just return it
341                         if(pic->flags & CACHEPICFLAG_NEWPIC)
342                                 return pic;
343                         if (!((pic->texflags ^ texflags) & ~(TEXF_COMPRESS))) // ignore TEXF_COMPRESS when comparing, because fallback pics remove the flag
344                         {
345                                 if(!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
346                                 {
347                                         if(pic->tex)
348                                                 pic->autoload = false; // persist it
349                                         else
350                                                 goto reload; // load it below, and then persist
351                                 }
352                                 return pic;
353                         }
354                 }
355         }
356
357         if (numcachepics == MAX_CACHED_PICS)
358         {
359                 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
360                 // FIXME: support NULL in callers?
361                 return cachepics; // return the first one
362         }
363         pic = cachepics + (numcachepics++);
364         memset(pic, 0, sizeof(*pic));
365         strlcpy (pic->name, path, sizeof(pic->name));
366         // link into list
367         pic->chain = cachepichash[hashkey];
368         cachepichash[hashkey] = pic;
369
370 reload:
371         // TODO why does this crash?
372         if(pic->allow_free_tex && pic->tex)
373                 R_PurgeTexture(pic->tex);
374
375         // check whether it is an dynamic texture (if so, we can directly use its texture handler)
376         pic->flags = cachepicflags;
377         pic->tex = CL_GetDynTexture( path );
378         // if so, set the width/height, too
379         if( pic->tex ) {
380                 pic->allow_free_tex = false;
381                 pic->width = R_TextureWidth(pic->tex);
382                 pic->height = R_TextureHeight(pic->tex);
383                 // we're done now (early-out)
384                 return pic;
385         }
386
387         pic->allow_free_tex = true;
388
389         pic->hasalpha = true; // assume alpha unless we know it has none
390         pic->texflags = texflags;
391         pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
392         pic->lastusedframe = draw_frame;
393
394         // load a high quality image from disk if possible
395         if (!loaded && r_texture_dds_load.integer != 0 && (pic->tex = R_LoadTextureDDSFile(drawtexturepool, va("dds/%s.dds", pic->name), vid.sRGB2D, pic->texflags, &ddshasalpha, ddsavgcolor, 0)))
396         {
397                 // note this loads even if autoload is true, otherwise we can't get the width/height
398                 loaded = true;
399                 pic->hasalpha = ddshasalpha;
400                 pic->width = R_TextureWidth(pic->tex);
401                 pic->height = R_TextureHeight(pic->tex);
402         }
403         if (!loaded && ((pixels = loadimagepixelsbgra(pic->name, false, true, false, NULL)) || (!strncmp(pic->name, "gfx/", 4) && (pixels = loadimagepixelsbgra(pic->name+4, false, true, false, NULL)))))
404         {
405                 loaded = true;
406                 pic->hasalpha = false;
407                 if (pic->texflags & TEXF_ALPHA)
408                 {
409                         for (j = 3;j < image_width * image_height * 4;j += 4)
410                         {
411                                 if (pixels[j] < 255)
412                                 {
413                                         pic->hasalpha = true;
414                                         break;
415                                 }
416                         }
417                 }
418
419                 pic->width = image_width;
420                 pic->height = image_height;
421                 if (!pic->autoload)
422                 {
423                         pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, image_width, image_height, pixels, vid.sRGB2D ? TEXTYPE_SRGB_BGRA : TEXTYPE_BGRA, pic->texflags & (pic->hasalpha ? ~0 : ~TEXF_ALPHA), -1, NULL);
424 #ifndef USE_GLES2
425                         if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
426                                 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
427 #endif
428                 }
429         }
430         if (!loaded)
431         {
432                 pic->autoload = false;
433                 // never compress the fallback images
434                 pic->texflags &= ~TEXF_COMPRESS;
435         }
436
437         // now read the low quality version (wad or lmp file), and take the pic
438         // size from that even if we don't upload the texture, this way the pics
439         // show up the right size in the menu even if they were replaced with
440         // higher or lower resolution versions
441         dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", pic->name);
442         if (!strncmp(pic->name, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
443         {
444                 if (developer_loading.integer)
445                         Con_Printf("loading lump \"%s\"\n", pic->name);
446
447                 if (lmpsize >= 9)
448                 {
449                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
450                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
451                         // if no high quality replacement image was found, upload the original low quality texture
452                         if (!loaded)
453                         {
454                                 loaded = true;
455                                 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, pic->width, pic->height, lmpdata + 8, vid.sRGB2D ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
456                         }
457                 }
458                 Mem_Free(lmpdata);
459         }
460         else if ((lmpdata = W_GetLumpName (pic->name + 4)))
461         {
462                 if (developer_loading.integer)
463                         Con_Printf("loading gfx.wad lump \"%s\"\n", pic->name + 4);
464
465                 if (!strcmp(pic->name, "gfx/conchars"))
466                 {
467                         // conchars is a raw image and with color 0 as transparent instead of 255
468                         pic->width = 128;
469                         pic->height = 128;
470                         // if no high quality replacement image was found, upload the original low quality texture
471                         if (!loaded)
472                         {
473                                 loaded = true;
474                                 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, 128, 128, lmpdata, vid.sRGB2D != 0 ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
475                         }
476                 }
477                 else
478                 {
479                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
480                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
481                         // if no high quality replacement image was found, upload the original low quality texture
482                         if (!loaded)
483                         {
484                                 loaded = true;
485                                 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, pic->width, pic->height, lmpdata + 8, vid.sRGB2D != 0 ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
486                         }
487                 }
488         }
489
490         if (pixels)
491         {
492                 Mem_Free(pixels);
493                 pixels = NULL;
494         }
495         if (!loaded)
496         {
497                 // if it's not found on disk, generate an image
498                 pic->tex = draw_generatepic(pic->name, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
499                 pic->width = R_TextureWidth(pic->tex);
500                 pic->height = R_TextureHeight(pic->tex);
501                 pic->allow_free_tex = (pic->tex != r_texture_notexture);
502         }
503
504         return pic;
505 }
506
507 cachepic_t *Draw_CachePic (const char *path)
508 {
509         return Draw_CachePic_Flags (path, 0); // default to persistent!
510 }
511
512 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
513 {
514         if (pic->autoload && !pic->tex)
515         {
516                 if (pic->tex == NULL && r_texture_dds_load.integer != 0)
517                 {
518                         qboolean ddshasalpha;
519                         float ddsavgcolor[4];
520                         pic->tex = R_LoadTextureDDSFile(drawtexturepool, va("dds/%s.dds", pic->name), vid.sRGB2D, pic->texflags, &ddshasalpha, ddsavgcolor, 0);
521                 }
522                 if (pic->tex == NULL)
523                 {
524                         pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, vid.sRGB2D);
525 #ifndef USE_GLES2
526                         if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
527                                 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
528 #endif
529                 }
530                 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
531                 {
532                         pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, vid.sRGB2D);
533 #ifndef USE_GLES2
534                         if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
535                                 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
536 #endif
537                 }
538                 if (pic->tex == NULL)
539                         pic->tex = draw_generatepic(pic->name, true);
540         }
541         pic->lastusedframe = draw_frame;
542         return pic->tex;
543 }
544
545 void Draw_Frame(void)
546 {
547         int i;
548         cachepic_t *pic;
549         static double nextpurgetime;
550         if (nextpurgetime > realtime)
551                 return;
552         nextpurgetime = realtime + 0.05;
553         for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
554         {
555                 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
556                 {
557                         R_FreeTexture(pic->tex);
558                         pic->tex = NULL;
559                 }
560         }
561         draw_frame++;
562 }
563
564 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
565 {
566         int crc, hashkey;
567         cachepic_t *pic;
568
569         crc = CRC_Block((unsigned char *)picname, strlen(picname));
570         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
571         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
572                 if (!strcmp (picname, pic->name))
573                         break;
574
575         if (pic)
576         {
577                 if (pic->flags == CACHEPICFLAG_NEWPIC && pic->tex && pic->width == width && pic->height == height)
578                 {
579                         R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, 0, width, height, 1);
580                         return pic;
581                 }
582         }
583         else
584         {
585                 if (numcachepics == MAX_CACHED_PICS)
586                 {
587                         Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
588                         // FIXME: support NULL in callers?
589                         return cachepics; // return the first one
590                 }
591                 pic = cachepics + (numcachepics++);
592                 memset(pic, 0, sizeof(*pic));
593                 strlcpy (pic->name, picname, sizeof(pic->name));
594                 // link into list
595                 pic->chain = cachepichash[hashkey];
596                 cachepichash[hashkey] = pic;
597         }
598
599         pic->flags = CACHEPICFLAG_NEWPIC; // disable texflags checks in Draw_CachePic
600         pic->width = width;
601         pic->height = height;
602         if (pic->allow_free_tex && pic->tex)
603                 R_FreeTexture(pic->tex);
604         pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0), -1, NULL);
605         return pic;
606 }
607
608 void Draw_FreePic(const char *picname)
609 {
610         int crc;
611         int hashkey;
612         cachepic_t *pic;
613         // this doesn't really free the pic, but does free it's texture
614         crc = CRC_Block((unsigned char *)picname, strlen(picname));
615         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
616         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
617         {
618                 if (!strcmp (picname, pic->name) && pic->tex)
619                 {
620                         R_FreeTexture(pic->tex);
621                         pic->tex = NULL;
622                         pic->width = 0;
623                         pic->height = 0;
624                         return;
625                 }
626         }
627 }
628
629 static float snap_to_pixel_x(float x, float roundUpAt);
630 extern int con_linewidth; // to force rewrapping
631 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
632 {
633         int i, ch;
634         float maxwidth;
635         char widthfile[MAX_QPATH];
636         char *widthbuf;
637         fs_offset_t widthbufsize;
638
639         if(override || !fnt->texpath[0])
640         {
641                 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
642                 // load the cvars when the font is FIRST loader
643                 fnt->settings.scale = scale;
644                 fnt->settings.voffset = voffset;
645                 fnt->settings.antialias = r_font_antialias.integer;
646                 fnt->settings.hinting = r_font_hinting.integer;
647                 fnt->settings.outline = r_font_postprocess_outline.value;
648                 fnt->settings.blur = r_font_postprocess_blur.value;
649                 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
650                 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
651                 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
652         }
653         // fix bad scale
654         if (fnt->settings.scale <= 0)
655                 fnt->settings.scale = 1;
656
657         if(drawtexturepool == NULL)
658                 return; // before gl_draw_start, so will be loaded later
659
660         if(fnt->ft2)
661         {
662                 // clear freetype font
663                 Font_UnloadFont(fnt->ft2);
664                 Mem_Free(fnt->ft2);
665                 fnt->ft2 = NULL;
666         }
667
668         if(fnt->req_face != -1)
669         {
670                 if(!Font_LoadFont(fnt->texpath, fnt))
671                         Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
672         }
673
674         fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
675         if(fnt->tex == r_texture_notexture)
676         {
677                 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
678                 {
679                         if (!fnt->fallbacks[i][0])
680                                 break;
681                         fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
682                         if(fnt->tex != r_texture_notexture)
683                                 break;
684                 }
685                 if(fnt->tex == r_texture_notexture)
686                 {
687                         fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
688                         strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
689                 }
690                 else
691                         dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
692         }
693         else
694                 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
695
696         // unspecified width == 1 (base width)
697         for(ch = 0; ch < 256; ++ch)
698                 fnt->width_of[ch] = 1;
699
700         // FIXME load "name.width", if it fails, fill all with 1
701         if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
702         {
703                 float extraspacing = 0;
704                 const char *p = widthbuf;
705
706                 ch = 0;
707                 while(ch < 256)
708                 {
709                         if(!COM_ParseToken_Simple(&p, false, false, true))
710                                 return;
711
712                         switch(*com_token)
713                         {
714                                 case '0':
715                                 case '1':
716                                 case '2':
717                                 case '3':
718                                 case '4':
719                                 case '5':
720                                 case '6':
721                                 case '7':
722                                 case '8':
723                                 case '9':
724                                 case '+':
725                                 case '-':
726                                 case '.':
727                                         fnt->width_of[ch] = atof(com_token) + extraspacing;
728                                         ch++;
729                                         break;
730                                 default:
731                                         if(!strcmp(com_token, "extraspacing"))
732                                         {
733                                                 if(!COM_ParseToken_Simple(&p, false, false, true))
734                                                         return;
735                                                 extraspacing = atof(com_token);
736                                         }
737                                         else if(!strcmp(com_token, "scale"))
738                                         {
739                                                 if(!COM_ParseToken_Simple(&p, false, false, true))
740                                                         return;
741                                                 fnt->settings.scale = atof(com_token);
742                                         }
743                                         else
744                                         {
745                                                 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
746                                                 if(!COM_ParseToken_Simple(&p, false, false, true))
747                                                         return;
748                                         }
749                                         break;
750                         }
751                 }
752
753                 Mem_Free(widthbuf);
754         }
755
756         if(fnt->ft2)
757         {
758                 for (i = 0; i < MAX_FONT_SIZES; ++i)
759                 {
760                         ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
761                         if (!map)
762                                 break;
763                         for(ch = 0; ch < 256; ++ch)
764                                 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
765                 }
766         }
767
768         maxwidth = fnt->width_of[0];
769         for(i = 1; i < 256; ++i)
770                 maxwidth = max(maxwidth, fnt->width_of[i]);
771         fnt->maxwidth = maxwidth;
772
773         // fix up maxwidth for overlap
774         fnt->maxwidth *= fnt->settings.scale;
775
776         if(fnt == FONT_CONSOLE)
777                 con_linewidth = -1; // rewrap console in next frame
778 }
779
780 extern cvar_t developer_font;
781 dp_font_t *FindFont(const char *title, qboolean allocate_new)
782 {
783         int i, oldsize;
784
785         // find font
786         for(i = 0; i < dp_fonts.maxsize; ++i)
787                 if(!strcmp(dp_fonts.f[i].title, title))
788                         return &dp_fonts.f[i];
789         // if not found - try allocate
790         if (allocate_new)
791         {
792                 // find any font with empty title
793                 for(i = 0; i < dp_fonts.maxsize; ++i)
794                 {
795                         if(!strcmp(dp_fonts.f[i].title, ""))
796                         {
797                                 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
798                                 return &dp_fonts.f[i];
799                         }
800                 }
801                 // if no any 'free' fonts - expand buffer
802                 oldsize = dp_fonts.maxsize;
803                 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
804                 if (developer_font.integer)
805                         Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", oldsize, dp_fonts.maxsize);
806                 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
807                 // relink ft2 structures
808                 for(i = 0; i < oldsize; ++i)
809                         if (dp_fonts.f[i].ft2)
810                                 dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings;
811                 // register a font in first expanded slot
812                 strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
813                 return &dp_fonts.f[oldsize];
814         }
815         return NULL;
816 }
817
818 static float snap_to_pixel_x(float x, float roundUpAt)
819 {
820         float pixelpos = x * vid.width / vid_conwidth.value;
821         int snap = (int) pixelpos;
822         if (pixelpos - snap >= roundUpAt) ++snap;
823         return ((float)snap * vid_conwidth.value / vid.width);
824         /*
825         x = (int)(x * vid.width / vid_conwidth.value);
826         x = (x * vid_conwidth.value / vid.width);
827         return x;
828         */
829 }
830
831 static float snap_to_pixel_y(float y, float roundUpAt)
832 {
833         float pixelpos = y * vid.height / vid_conheight.value;
834         int snap = (int) pixelpos;
835         if (pixelpos - snap > roundUpAt) ++snap;
836         return ((float)snap * vid_conheight.value / vid.height);
837         /*
838         y = (int)(y * vid.height / vid_conheight.value);
839         y = (y * vid_conheight.value / vid.height);
840         return y;
841         */
842 }
843
844 static void LoadFont_f(void)
845 {
846         dp_font_t *f;
847         int i, sizes;
848         const char *filelist, *c, *cm;
849         float sz, scale, voffset;
850         char mainfont[MAX_QPATH];
851
852         if(Cmd_Argc() < 2)
853         {
854                 Con_Printf("Available font commands:\n");
855                 for(i = 0; i < dp_fonts.maxsize; ++i)
856                         if (dp_fonts.f[i].title[0])
857                                 Con_Printf("  loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
858                 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
859                            "can specify multiple fonts and faces\n"
860                            "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
861                            "to load face 2 of the font gfx/vera-sans and use face 1\n"
862                            "of gfx/fallback as fallback font.\n"
863                            "You can also specify a list of font sizes to load, like this:\n"
864                            "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
865                            "In many cases, 8 12 16 24 32 should be a good choice.\n"
866                            "custom switches:\n"
867                            " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
868                            " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
869                         );
870                 return;
871         }
872         f = FindFont(Cmd_Argv(1), true);
873         if(f == NULL)
874         {
875                 Con_Printf("font function not found\n");
876                 return;
877         }
878
879         if(Cmd_Argc() < 3)
880                 filelist = "gfx/conchars";
881         else
882                 filelist = Cmd_Argv(2);
883
884         memset(f->fallbacks, 0, sizeof(f->fallbacks));
885         memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
886
887         // first font is handled "normally"
888         c = strchr(filelist, ':');
889         cm = strchr(filelist, ',');
890         if(c && (!cm || c < cm))
891                 f->req_face = atoi(c+1);
892         else
893         {
894                 f->req_face = 0;
895                 c = cm;
896         }
897
898         if(!c || (c - filelist) > MAX_QPATH)
899                 strlcpy(mainfont, filelist, sizeof(mainfont));
900         else
901         {
902                 memcpy(mainfont, filelist, c - filelist);
903                 mainfont[c - filelist] = 0;
904         }
905
906         for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
907         {
908                 c = strchr(filelist, ',');
909                 if(!c)
910                         break;
911                 filelist = c + 1;
912                 if(!*filelist)
913                         break;
914                 c = strchr(filelist, ':');
915                 cm = strchr(filelist, ',');
916                 if(c && (!cm || c < cm))
917                         f->fallback_faces[i] = atoi(c+1);
918                 else
919                 {
920                         f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
921                         c = cm;
922                 }
923                 if(!c || (c-filelist) > MAX_QPATH)
924                 {
925                         strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
926                 }
927                 else
928                 {
929                         memcpy(f->fallbacks[i], filelist, c - filelist);
930                         f->fallbacks[i][c - filelist] = 0;
931                 }
932         }
933
934         // for now: by default load only one size: the default size
935         f->req_sizes[0] = 0;
936         for(i = 1; i < MAX_FONT_SIZES; ++i)
937                 f->req_sizes[i] = -1;
938
939         scale = 1;
940         voffset = 0;
941         if(Cmd_Argc() >= 4)
942         {
943                 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
944                 {
945                         // special switches
946                         if (!strcmp(Cmd_Argv(i), "scale"))
947                         {
948                                 i++;
949                                 if (i < Cmd_Argc())
950                                         scale = atof(Cmd_Argv(i));
951                                 continue;
952                         }
953                         if (!strcmp(Cmd_Argv(i), "voffset"))
954                         {
955                                 i++;
956                                 if (i < Cmd_Argc())
957                                         voffset = atof(Cmd_Argv(i));
958                                 continue;
959                         }
960
961                         if (sizes == -1)
962                                 continue; // no slot for other sizes
963
964                         // parse one of sizes
965                         sz = atof(Cmd_Argv(i));
966                         if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
967                         {
968                                 // search for duplicated sizes
969                                 int j;
970                                 for (j=0; j<sizes; j++)
971                                         if (f->req_sizes[j] == sz)
972                                                 break;
973                                 if (j != sizes)
974                                         continue; // sz already in req_sizes, don't add it again
975
976                                 if (sizes == MAX_FONT_SIZES)
977                                 {
978                                         Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
979                                         sizes = -1;
980                                         continue;
981                                 }
982                                 f->req_sizes[sizes] = sz;
983                                 sizes++;
984                         }
985                 }
986         }
987
988         LoadFont(true, mainfont, f, scale, voffset);
989 }
990
991 /*
992 ===============
993 Draw_Init
994 ===============
995 */
996 static void gl_draw_start(void)
997 {
998         int i;
999         drawtexturepool = R_AllocTexturePool();
1000
1001         numcachepics = 0;
1002         memset(cachepichash, 0, sizeof(cachepichash));
1003
1004         font_start();
1005
1006         // load default font textures
1007         for(i = 0; i < dp_fonts.maxsize; ++i)
1008                 if (dp_fonts.f[i].title[0])
1009                         LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
1010
1011         // draw the loading screen so people have something to see in the newly opened window
1012         SCR_UpdateLoadingScreen(true);
1013 }
1014
1015 static void gl_draw_shutdown(void)
1016 {
1017         font_shutdown();
1018
1019         R_FreeTexturePool(&drawtexturepool);
1020
1021         numcachepics = 0;
1022         memset(cachepichash, 0, sizeof(cachepichash));
1023 }
1024
1025 static void gl_draw_newmap(void)
1026 {
1027         font_newmap();
1028 }
1029
1030 void GL_Draw_Init (void)
1031 {
1032         int i, j;
1033
1034         Cvar_RegisterVariable(&r_font_postprocess_blur);
1035         Cvar_RegisterVariable(&r_font_postprocess_outline);
1036         Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
1037         Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
1038         Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
1039         Cvar_RegisterVariable(&r_font_hinting);
1040         Cvar_RegisterVariable(&r_font_antialias);
1041         Cvar_RegisterVariable(&r_textshadow);
1042         Cvar_RegisterVariable(&r_textbrightness);
1043         Cvar_RegisterVariable(&r_textcontrast);
1044
1045         // allocate fonts storage
1046         fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
1047         dp_fonts.maxsize = MAX_FONTS;
1048         dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
1049         memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
1050
1051         // assign starting font names
1052         strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
1053         strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
1054         strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
1055         strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
1056         strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
1057         strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
1058         strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
1059         strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
1060         strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
1061         for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
1062                 if(!FONT_USER(i)->title[0])
1063                         dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
1064
1065         Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
1066         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
1067 }
1068
1069 static void _DrawQ_Setup(void) // see R_ResetViewRendering2D
1070 {
1071         if (r_refdef.draw2dstage == 1)
1072                 return;
1073         r_refdef.draw2dstage = 1;
1074
1075         R_ResetViewRendering2D_Common(0, NULL, NULL, vid_conwidth.integer, vid_conheight.integer);
1076 }
1077
1078 qboolean r_draw2d_force = false;
1079 void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1080 {
1081         _DrawQ_Setup();
1082         if(!r_draw2d.integer && !r_draw2d_force)
1083                 return;
1084         DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1085 }
1086 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1087 {
1088         if(flags == DRAWFLAG_ADDITIVE)
1089         {
1090                 GL_DepthMask(false);
1091                 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1092         }
1093         else if(flags == DRAWFLAG_MODULATE)
1094         {
1095                 GL_DepthMask(false);
1096                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1097         }
1098         else if(flags == DRAWFLAG_2XMODULATE)
1099         {
1100                 GL_DepthMask(false);
1101                 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1102         }
1103         else if(flags == DRAWFLAG_SCREEN)
1104         {
1105                 GL_DepthMask(false);
1106                 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1107         }
1108         else if(alpha)
1109         {
1110                 GL_DepthMask(false);
1111                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1112         }
1113         else
1114         {
1115                 GL_DepthMask(true);
1116                 GL_BlendFunc(GL_ONE, GL_ZERO);
1117         }
1118 }
1119
1120 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1121 {
1122         float floats[36];
1123
1124         _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1125         if(!r_draw2d.integer && !r_draw2d_force)
1126                 return;
1127
1128 //      R_Mesh_ResetTextureState();
1129         floats[12] = 0.0f;floats[13] = 0.0f;
1130         floats[14] = 1.0f;floats[15] = 0.0f;
1131         floats[16] = 1.0f;floats[17] = 1.0f;
1132         floats[18] = 0.0f;floats[19] = 1.0f;
1133         floats[20] = floats[24] = floats[28] = floats[32] = red;
1134         floats[21] = floats[25] = floats[29] = floats[33] = green;
1135         floats[22] = floats[26] = floats[30] = floats[34] = blue;
1136         floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1137         if (pic)
1138         {
1139                 if (width == 0)
1140                         width = pic->width;
1141                 if (height == 0)
1142                         height = pic->height;
1143                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1144
1145 #if 0
1146       // AK07: lets be texel correct on the corners
1147       {
1148          float horz_offset = 0.5f / pic->width;
1149          float vert_offset = 0.5f / pic->height;
1150
1151                    floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1152                    floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1153                    floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1154                    floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1155       }
1156 #endif
1157         }
1158         else
1159                 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1160
1161         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1162         floats[0] = floats[9] = x;
1163         floats[1] = floats[4] = y;
1164         floats[3] = floats[6] = x + width;
1165         floats[7] = floats[10] = y + height;
1166
1167         R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1168         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1169 }
1170
1171 void DrawQ_RotPic(float x, float y, cachepic_t *pic, float width, float height, float org_x, float org_y, float angle, float red, float green, float blue, float alpha, int flags)
1172 {
1173         float floats[36];
1174         float af = DEG2RAD(-angle); // forward
1175         float ar = DEG2RAD(-angle + 90); // right
1176         float sinaf = sin(af);
1177         float cosaf = cos(af);
1178         float sinar = sin(ar);
1179         float cosar = cos(ar);
1180
1181         _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1182         if(!r_draw2d.integer && !r_draw2d_force)
1183                 return;
1184
1185 //      R_Mesh_ResetTextureState();
1186         if (pic)
1187         {
1188                 if (width == 0)
1189                         width = pic->width;
1190                 if (height == 0)
1191                         height = pic->height;
1192                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1193         }
1194         else
1195                 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1196
1197         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1198
1199 // top left
1200         floats[0] = x - cosaf*org_x - cosar*org_y;
1201         floats[1] = y - sinaf*org_x - sinar*org_y;
1202
1203 // top right
1204         floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1205         floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1206
1207 // bottom right
1208         floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1209         floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1210
1211 // bottom left
1212         floats[9]  = x - cosaf*org_x + cosar*(height-org_y);
1213         floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1214
1215         floats[12] = 0.0f;floats[13] = 0.0f;
1216         floats[14] = 1.0f;floats[15] = 0.0f;
1217         floats[16] = 1.0f;floats[17] = 1.0f;
1218         floats[18] = 0.0f;floats[19] = 1.0f;
1219         floats[20] = floats[24] = floats[28] = floats[32] = red;
1220         floats[21] = floats[25] = floats[29] = floats[33] = green;
1221         floats[22] = floats[26] = floats[30] = floats[34] = blue;
1222         floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1223
1224         R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1225         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1226 }
1227
1228 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1229 {
1230         float floats[36];
1231
1232         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1233         if(!r_draw2d.integer && !r_draw2d_force)
1234                 return;
1235
1236 //      R_Mesh_ResetTextureState();
1237         R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1238
1239         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1240         floats[0] = floats[9] = x;
1241         floats[1] = floats[4] = y;
1242         floats[3] = floats[6] = x + width;
1243         floats[7] = floats[10] = y + height;
1244         floats[12] = 0.0f;floats[13] = 0.0f;
1245         floats[14] = 1.0f;floats[15] = 0.0f;
1246         floats[16] = 1.0f;floats[17] = 1.0f;
1247         floats[18] = 0.0f;floats[19] = 1.0f;
1248         floats[20] = floats[24] = floats[28] = floats[32] = red;
1249         floats[21] = floats[25] = floats[29] = floats[33] = green;
1250         floats[22] = floats[26] = floats[30] = floats[34] = blue;
1251         floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1252
1253         R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1254         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1255 }
1256
1257 /// color tag printing
1258 static const vec4_t string_colors[] =
1259 {
1260         // Quake3 colors
1261         // LordHavoc: why on earth is cyan before magenta in Quake3?
1262         // LordHavoc: note: Doom3 uses white for [0] and [7]
1263         {0.0, 0.0, 0.0, 1.0}, // black
1264         {1.0, 0.0, 0.0, 1.0}, // red
1265         {0.0, 1.0, 0.0, 1.0}, // green
1266         {1.0, 1.0, 0.0, 1.0}, // yellow
1267         {0.0, 0.0, 1.0, 1.0}, // blue
1268         {0.0, 1.0, 1.0, 1.0}, // cyan
1269         {1.0, 0.0, 1.0, 1.0}, // magenta
1270         {1.0, 1.0, 1.0, 1.0}, // white
1271         // [515]'s BX_COLOREDTEXT extension
1272         {1.0, 1.0, 1.0, 0.5}, // half transparent
1273         {0.5, 0.5, 0.5, 1.0}  // half brightness
1274         // Black's color table
1275         //{1.0, 1.0, 1.0, 1.0},
1276         //{1.0, 0.0, 0.0, 1.0},
1277         //{0.0, 1.0, 0.0, 1.0},
1278         //{0.0, 0.0, 1.0, 1.0},
1279         //{1.0, 1.0, 0.0, 1.0},
1280         //{0.0, 1.0, 1.0, 1.0},
1281         //{1.0, 0.0, 1.0, 1.0},
1282         //{0.1, 0.1, 0.1, 1.0}
1283 };
1284
1285 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
1286
1287 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1288 {
1289         float C = r_textcontrast.value;
1290         float B = r_textbrightness.value;
1291         if (colorindex & 0x10000) // that bit means RGB color
1292         {
1293                 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1294                 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1295                 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1296                 color[3] = (colorindex & 0xf) / 15.0;
1297         }
1298         else
1299                 Vector4Copy(string_colors[colorindex], color);
1300         Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1301         if (shadow)
1302         {
1303                 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1304                 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1305         }
1306 }
1307
1308 // NOTE: this function always draws exactly one character if maxwidth <= 0
1309 float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *maxlen, float w, float h, float sw, float sh, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
1310 {
1311         const char *text_start = text;
1312         int colorindex = STRING_COLOR_DEFAULT;
1313         size_t i;
1314         float x = 0;
1315         Uchar ch, mapch, nextch;
1316         Uchar prevch = 0; // used for kerning
1317         int tempcolorindex;
1318         float kx;
1319         int map_index = 0;
1320         size_t bytes_left;
1321         ft2_font_map_t *fontmap = NULL;
1322         ft2_font_map_t *map = NULL;
1323         //ft2_font_map_t *prevmap = NULL;
1324         ft2_font_t *ft2 = fnt->ft2;
1325         // float ftbase_x;
1326         qboolean snap = true;
1327         qboolean least_one = false;
1328         float dw; // display w
1329         //float dh; // display h
1330         const float *width_of;
1331
1332         if (!h) h = w;
1333         if (!h) {
1334                 w = h = 1;
1335                 snap = false;
1336         }
1337         // do this in the end
1338         w *= fnt->settings.scale;
1339         h *= fnt->settings.scale;
1340
1341         // find the most fitting size:
1342         if (ft2 != NULL)
1343         {
1344                 if (snap)
1345                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1346                 else
1347                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1348                 fontmap = Font_MapForIndex(ft2, map_index);
1349         }
1350
1351         dw = w * sw;
1352         //dh = h * sh;
1353
1354         if (*maxlen < 1)
1355                 *maxlen = 1<<30;
1356
1357         if (!outcolor || *outcolor == -1)
1358                 colorindex = STRING_COLOR_DEFAULT;
1359         else
1360                 colorindex = *outcolor;
1361
1362         // maxwidth /= fnt->scale; // w and h are multiplied by it already
1363         // ftbase_x = snap_to_pixel_x(0);
1364         
1365         if(maxwidth <= 0)
1366         {
1367                 least_one = true;
1368                 maxwidth = -maxwidth;
1369         }
1370
1371         //if (snap)
1372         //      x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1373
1374         if (fontmap)
1375                 width_of = fontmap->width_of;
1376         else
1377                 width_of = fnt->width_of;
1378
1379         for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1380         {
1381                 size_t i0 = i;
1382                 nextch = ch = u8_getnchar(text, &text, bytes_left);
1383                 i = text - text_start;
1384                 if (!ch)
1385                         break;
1386                 if (ch == ' ' && !fontmap)
1387                 {
1388                         if(!least_one || i0) // never skip the first character
1389                         if(x + width_of[(int) ' '] * dw > maxwidth)
1390                         {
1391                                 i = i0;
1392                                 break; // oops, can't draw this
1393                         }
1394                         x += width_of[(int) ' '] * dw;
1395                         continue;
1396                 }
1397                 // i points to the char after ^
1398                 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1399                 {
1400                         ch = *text; // colors are ascii, so no u8_ needed
1401                         if (ch <= '9' && ch >= '0') // ^[0-9] found
1402                         {
1403                                 colorindex = ch - '0';
1404                                 ++text;
1405                                 ++i;
1406                                 continue;
1407                         }
1408                         // i points to the char after ^...
1409                         // i+3 points to 3 in ^x123
1410                         // i+3 == *maxlen would mean that char is missing
1411                         else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1412                         {
1413                                 // building colorindex...
1414                                 ch = tolower(text[1]);
1415                                 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1416                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1417                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1418                                 else tempcolorindex = 0;
1419                                 if (tempcolorindex)
1420                                 {
1421                                         ch = tolower(text[2]);
1422                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1423                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1424                                         else tempcolorindex = 0;
1425                                         if (tempcolorindex)
1426                                         {
1427                                                 ch = tolower(text[3]);
1428                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1429                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1430                                                 else tempcolorindex = 0;
1431                                                 if (tempcolorindex)
1432                                                 {
1433                                                         colorindex = tempcolorindex | 0xf;
1434                                                         // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1435                                                         i+=4;
1436                                                         text += 4;
1437                                                         continue;
1438                                                 }
1439                                         }
1440                                 }
1441                         }
1442                         else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1443                         {
1444                                 i++;
1445                                 text++;
1446                         }
1447                         i--;
1448                 }
1449                 ch = nextch;
1450
1451                 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1452                 {
1453                         if (ch > 0xE000)
1454                                 ch -= 0xE000;
1455                         if (ch > 0xFF)
1456                                 continue;
1457                         if (fontmap)
1458                                 map = ft2_oldstyle_map;
1459                         prevch = 0;
1460                         if(!least_one || i0) // never skip the first character
1461                         if(x + width_of[ch] * dw > maxwidth)
1462                         {
1463                                 i = i0;
1464                                 break; // oops, can't draw this
1465                         }
1466                         x += width_of[ch] * dw;
1467                 } else {
1468                         if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1469                         {
1470                                 map = FontMap_FindForChar(fontmap, ch);
1471                                 if (!map)
1472                                 {
1473                                         if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1474                                                 break;
1475                                         if (!map)
1476                                                 break;
1477                                 }
1478                         }
1479                         mapch = ch - map->start;
1480                         if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1481                                 x += kx * dw;
1482                         x += map->glyphs[mapch].advance_x * dw;
1483                         //prevmap = map;
1484                         prevch = ch;
1485                 }
1486         }
1487
1488         *maxlen = i;
1489
1490         if (outcolor)
1491                 *outcolor = colorindex;
1492
1493         return x;
1494 }
1495
1496 float DrawQ_Color[4];
1497 float DrawQ_String_Scale(float startx, float starty, const char *text, size_t maxlen, float w, float h, float sw, float sh, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt)
1498 {
1499         int shadow, colorindex = STRING_COLOR_DEFAULT;
1500         size_t i;
1501         float x = startx, y, s, t, u, v, thisw;
1502         float *av, *at, *ac;
1503         int batchcount;
1504         static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1505         static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1506         static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1507         Uchar ch, mapch, nextch;
1508         Uchar prevch = 0; // used for kerning
1509         int tempcolorindex;
1510         int map_index = 0;
1511         //ft2_font_map_t *prevmap = NULL; // the previous map
1512         ft2_font_map_t *map = NULL;     // the currently used map
1513         ft2_font_map_t *fontmap = NULL; // the font map for the size
1514         float ftbase_y;
1515         const char *text_start = text;
1516         float kx, ky;
1517         ft2_font_t *ft2 = fnt->ft2;
1518         qboolean snap = true;
1519         float pix_x, pix_y;
1520         size_t bytes_left;
1521         float dw, dh;
1522         const float *width_of;
1523
1524         int tw, th;
1525         tw = R_TextureWidth(fnt->tex);
1526         th = R_TextureHeight(fnt->tex);
1527
1528         if (!h) h = w;
1529         if (!h) {
1530                 h = w = 1;
1531                 snap = false;
1532         }
1533
1534         starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1535         w *= fnt->settings.scale;
1536         h *= fnt->settings.scale;
1537
1538         if (ft2 != NULL)
1539         {
1540                 if (snap)
1541                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1542                 else
1543                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1544                 fontmap = Font_MapForIndex(ft2, map_index);
1545         }
1546
1547         dw = w * sw;
1548         dh = h * sh;
1549
1550         // draw the font at its baseline when using freetype
1551         //ftbase_x = 0;
1552         ftbase_y = dh * (4.5/6.0);
1553
1554         if (maxlen < 1)
1555                 maxlen = 1<<30;
1556
1557         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1558         if(!r_draw2d.integer && !r_draw2d_force)
1559                 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1560
1561 //      R_Mesh_ResetTextureState();
1562         if (!fontmap)
1563                 R_Mesh_TexBind(0, fnt->tex);
1564         R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1565
1566         ac = color4f;
1567         at = texcoord2f;
1568         av = vertex3f;
1569         batchcount = 0;
1570
1571         //ftbase_x = snap_to_pixel_x(ftbase_x);
1572         if(snap)
1573         {
1574                 startx = snap_to_pixel_x(startx, 0.4);
1575                 starty = snap_to_pixel_y(starty, 0.4);
1576                 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1577         }
1578
1579         pix_x = vid.width / vid_conwidth.value;
1580         pix_y = vid.height / vid_conheight.value;
1581
1582         if (fontmap)
1583                 width_of = fontmap->width_of;
1584         else
1585                 width_of = fnt->width_of;
1586
1587         for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1588         {
1589                 prevch = 0;
1590                 text = text_start;
1591
1592                 if (!outcolor || *outcolor == -1)
1593                         colorindex = STRING_COLOR_DEFAULT;
1594                 else
1595                         colorindex = *outcolor;
1596
1597                 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1598
1599                 x = startx;
1600                 y = starty;
1601                 /*
1602                 if (shadow)
1603                 {
1604                         x += r_textshadow.value * vid.width / vid_conwidth.value;
1605                         y += r_textshadow.value * vid.height / vid_conheight.value;
1606                 }
1607                 */
1608                 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1609                 {
1610                         nextch = ch = u8_getnchar(text, &text, bytes_left);
1611                         i = text - text_start;
1612                         if (!ch)
1613                                 break;
1614                         if (ch == ' ' && !fontmap)
1615                         {
1616                                 x += width_of[(int) ' '] * dw;
1617                                 continue;
1618                         }
1619                         if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1620                         {
1621                                 ch = *text; // colors are ascii, so no u8_ needed
1622                                 if (ch <= '9' && ch >= '0') // ^[0-9] found
1623                                 {
1624                                         colorindex = ch - '0';
1625                                         DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1626                                         ++text;
1627                                         ++i;
1628                                         continue;
1629                                 }
1630                                 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1631                                 {
1632                                         // building colorindex...
1633                                         ch = tolower(text[1]);
1634                                         tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1635                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1636                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1637                                         else tempcolorindex = 0;
1638                                         if (tempcolorindex)
1639                                         {
1640                                                 ch = tolower(text[2]);
1641                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1642                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1643                                                 else tempcolorindex = 0;
1644                                                 if (tempcolorindex)
1645                                                 {
1646                                                         ch = tolower(text[3]);
1647                                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1648                                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1649                                                         else tempcolorindex = 0;
1650                                                         if (tempcolorindex)
1651                                                         {
1652                                                                 colorindex = tempcolorindex | 0xf;
1653                                                                 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1654                                                                 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1655                                                                 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1656                                                                 i+=4;
1657                                                                 text+=4;
1658                                                                 continue;
1659                                                         }
1660                                                 }
1661                                         }
1662                                 }
1663                                 else if (ch == STRING_COLOR_TAG)
1664                                 {
1665                                         i++;
1666                                         text++;
1667                                 }
1668                                 i--;
1669                         }
1670                         // get the backup
1671                         ch = nextch;
1672                         // using a value of -1 for the oldstyle map because NULL means uninitialized...
1673                         // this way we don't need to rebind fnt->tex for every old-style character
1674                         // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1675                         if (shadow)
1676                         {
1677                                 x += 1.0/pix_x * r_textshadow.value;
1678                                 y += 1.0/pix_y * r_textshadow.value;
1679                         }
1680                         if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1681                         {
1682                                 if (ch >= 0xE000)
1683                                         ch -= 0xE000;
1684                                 if (ch > 0xFF)
1685                                         goto out;
1686                                 if (fontmap)
1687                                 {
1688                                         if (map != ft2_oldstyle_map)
1689                                         {
1690                                                 if (batchcount)
1691                                                 {
1692                                                         // switching from freetype to non-freetype rendering
1693                                                         R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1694                                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1695                                                         batchcount = 0;
1696                                                         ac = color4f;
1697                                                         at = texcoord2f;
1698                                                         av = vertex3f;
1699                                                 }
1700                                                 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1701                                                 map = ft2_oldstyle_map;
1702                                         }
1703                                 }
1704                                 prevch = 0;
1705                                 //num = (unsigned char) text[i];
1706                                 //thisw = fnt->width_of[num];
1707                                 thisw = fnt->width_of[ch];
1708                                 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1709                                 s = (ch & 15)*0.0625f + (0.5f / tw);
1710                                 t = (ch >> 4)*0.0625f + (0.5f / th);
1711                                 u = 0.0625f * thisw - (1.0f / tw);
1712                                 v = 0.0625f - (1.0f / th);
1713                                 ac[ 0] = DrawQ_Color[0];ac[ 1] = DrawQ_Color[1];ac[ 2] = DrawQ_Color[2];ac[ 3] = DrawQ_Color[3];
1714                                 ac[ 4] = DrawQ_Color[0];ac[ 5] = DrawQ_Color[1];ac[ 6] = DrawQ_Color[2];ac[ 7] = DrawQ_Color[3];
1715                                 ac[ 8] = DrawQ_Color[0];ac[ 9] = DrawQ_Color[1];ac[10] = DrawQ_Color[2];ac[11] = DrawQ_Color[3];
1716                                 ac[12] = DrawQ_Color[0];ac[13] = DrawQ_Color[1];ac[14] = DrawQ_Color[2];ac[15] = DrawQ_Color[3];
1717                                 at[ 0] = s              ; at[ 1] = t    ;
1718                                 at[ 2] = s+u    ; at[ 3] = t    ;
1719                                 at[ 4] = s+u    ; at[ 5] = t+v  ;
1720                                 at[ 6] = s              ; at[ 7] = t+v  ;
1721                                 av[ 0] = x                      ; av[ 1] = y    ; av[ 2] = 10;
1722                                 av[ 3] = x+dw*thisw     ; av[ 4] = y    ; av[ 5] = 10;
1723                                 av[ 6] = x+dw*thisw     ; av[ 7] = y+dh ; av[ 8] = 10;
1724                                 av[ 9] = x                      ; av[10] = y+dh ; av[11] = 10;
1725                                 ac += 16;
1726                                 at += 8;
1727                                 av += 12;
1728                                 batchcount++;
1729                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1730                                 {
1731                                         R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1732                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1733                                         batchcount = 0;
1734                                         ac = color4f;
1735                                         at = texcoord2f;
1736                                         av = vertex3f;
1737                                 }
1738                                 x += width_of[ch] * dw;
1739                         } else {
1740                                 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1741                                 {
1742                                         // new charmap - need to render
1743                                         if (batchcount)
1744                                         {
1745                                                 // we need a different character map, render what we currently have:
1746                                                 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1747                                                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1748                                                 batchcount = 0;
1749                                                 ac = color4f;
1750                                                 at = texcoord2f;
1751                                                 av = vertex3f;
1752                                         }
1753                                         // find the new map
1754                                         map = FontMap_FindForChar(fontmap, ch);
1755                                         if (!map)
1756                                         {
1757                                                 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1758                                                 {
1759                                                         shadow = -1;
1760                                                         break;
1761                                                 }
1762                                                 if (!map)
1763                                                 {
1764                                                         // this shouldn't happen
1765                                                         shadow = -1;
1766                                                         break;
1767                                                 }
1768                                         }
1769                                         R_SetupShader_Generic(map->pic->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1770                                 }
1771
1772                                 mapch = ch - map->start;
1773                                 thisw = map->glyphs[mapch].advance_x;
1774
1775                                 //x += ftbase_x;
1776                                 y += ftbase_y;
1777                                 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1778                                 {
1779                                         x += kx * dw;
1780                                         y += ky * dh;
1781                                 }
1782                                 else
1783                                         kx = ky = 0;
1784                                 ac[ 0] = DrawQ_Color[0]; ac[ 1] = DrawQ_Color[1]; ac[ 2] = DrawQ_Color[2]; ac[ 3] = DrawQ_Color[3];
1785                                 ac[ 4] = DrawQ_Color[0]; ac[ 5] = DrawQ_Color[1]; ac[ 6] = DrawQ_Color[2]; ac[ 7] = DrawQ_Color[3];
1786                                 ac[ 8] = DrawQ_Color[0]; ac[ 9] = DrawQ_Color[1]; ac[10] = DrawQ_Color[2]; ac[11] = DrawQ_Color[3];
1787                                 ac[12] = DrawQ_Color[0]; ac[13] = DrawQ_Color[1]; ac[14] = DrawQ_Color[2]; ac[15] = DrawQ_Color[3];
1788                                 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1789                                 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1790                                 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1791                                 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1792                                 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1793                                 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1794                                 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1795                                 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1796                                 //x -= ftbase_x;
1797                                 y -= ftbase_y;
1798
1799                                 x += thisw * dw;
1800                                 ac += 16;
1801                                 at += 8;
1802                                 av += 12;
1803                                 batchcount++;
1804                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1805                                 {
1806                                         R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1807                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1808                                         batchcount = 0;
1809                                         ac = color4f;
1810                                         at = texcoord2f;
1811                                         av = vertex3f;
1812                                 }
1813
1814                                 //prevmap = map;
1815                                 prevch = ch;
1816                         }
1817 out:
1818                         if (shadow)
1819                         {
1820                                 x -= 1.0/pix_x * r_textshadow.value;
1821                                 y -= 1.0/pix_y * r_textshadow.value;
1822                         }
1823                 }
1824         }
1825         if (batchcount > 0)
1826         {
1827                 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1828                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1829         }
1830
1831         if (outcolor)
1832                 *outcolor = colorindex;
1833         
1834         // note: this relies on the proper text (not shadow) being drawn last
1835         return x;
1836 }
1837
1838 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, const dp_font_t *fnt)
1839 {
1840         return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1841 }
1842
1843 float DrawQ_TextWidth_UntilWidth_TrackColors(const char *text, size_t *maxlen, float w, float h, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
1844 {
1845         return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1846 }
1847
1848 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1849 {
1850         return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1851 }
1852
1853 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1854 {
1855         return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1856 }
1857
1858 #if 0
1859 // not used
1860 // no ^xrgb management
1861 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1862 {
1863         int color, numchars = 0;
1864         char *outputend2c = output2c + maxoutchars - 2;
1865         if (!outcolor || *outcolor == -1)
1866                 color = STRING_COLOR_DEFAULT;
1867         else
1868                 color = *outcolor;
1869         if (!maxreadchars)
1870                 maxreadchars = 1<<30;
1871         textend = text + maxreadchars;
1872         while (text != textend && *text)
1873         {
1874                 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1875                 {
1876                         if (text[1] == STRING_COLOR_TAG)
1877                                 text++;
1878                         else if (text[1] >= '0' && text[1] <= '9')
1879                         {
1880                                 color = text[1] - '0';
1881                                 text += 2;
1882                                 continue;
1883                         }
1884                 }
1885                 if (output2c >= outputend2c)
1886                         break;
1887                 *output2c++ = *text++;
1888                 *output2c++ = color;
1889                 numchars++;
1890         }
1891         output2c[0] = output2c[1] = 0;
1892         if (outcolor)
1893                 *outcolor = color;
1894         return numchars;
1895 }
1896 #endif
1897
1898 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)
1899 {
1900         float floats[36];
1901
1902         _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1903         if(!r_draw2d.integer && !r_draw2d_force)
1904                 return;
1905
1906 //      R_Mesh_ResetTextureState();
1907         if (pic)
1908         {
1909                 if (width == 0)
1910                         width = pic->width;
1911                 if (height == 0)
1912                         height = pic->height;
1913                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1914         }
1915         else
1916                 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1917
1918         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1919         floats[0] = floats[9] = x;
1920         floats[1] = floats[4] = y;
1921         floats[3] = floats[6] = x + width;
1922         floats[7] = floats[10] = y + height;
1923         floats[12] = s1;floats[13] = t1;
1924         floats[14] = s2;floats[15] = t2;
1925         floats[16] = s4;floats[17] = t4;
1926         floats[18] = s3;floats[19] = t3;
1927         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1928         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1929         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1930         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1931
1932         R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1933         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1934 }
1935
1936 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1937 {
1938         _DrawQ_Setup();
1939         if(!r_draw2d.integer && !r_draw2d_force)
1940                 return;
1941         DrawQ_ProcessDrawFlag(flags, hasalpha);
1942
1943 //      R_Mesh_ResetTextureState();
1944         R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1945
1946         R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1947         R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1948 }
1949
1950 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1951 {
1952         int num;
1953
1954         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1955         if(!r_draw2d.integer && !r_draw2d_force)
1956                 return;
1957
1958         GL_Color(1,1,1,1);
1959         switch(vid.renderpath)
1960         {
1961         case RENDERPATH_GL11:
1962         case RENDERPATH_GL13:
1963         case RENDERPATH_GL20:
1964 #ifndef USE_GLES2
1965                 CHECKGLERROR
1966                 qglBegin(GL_LINE_LOOP);
1967                 for (num = 0;num < mesh->num_vertices;num++)
1968                 {
1969                         if (mesh->data_color4f)
1970                                 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]);
1971                         qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1972                 }
1973                 qglEnd();
1974                 CHECKGLERROR
1975 #endif
1976                 break;
1977         case RENDERPATH_D3D9:
1978                 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1979                 break;
1980         case RENDERPATH_D3D10:
1981                 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1982                 break;
1983         case RENDERPATH_D3D11:
1984                 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1985                 break;
1986         case RENDERPATH_SOFT:
1987                 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1988                 break;
1989         case RENDERPATH_GLES1:
1990         case RENDERPATH_GLES2:
1991                 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1992                 return;
1993         }
1994 }
1995
1996 //[515]: this is old, delete
1997 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1998 {
1999         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
2000         if(!r_draw2d.integer && !r_draw2d_force)
2001                 return;
2002
2003         R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
2004
2005         switch(vid.renderpath)
2006         {
2007         case RENDERPATH_GL11:
2008         case RENDERPATH_GL13:
2009         case RENDERPATH_GL20:
2010 #ifndef USE_GLES2
2011                 CHECKGLERROR
2012
2013                 //qglLineWidth(width);CHECKGLERROR
2014
2015                 GL_Color(r,g,b,alpha);
2016                 CHECKGLERROR
2017                 qglBegin(GL_LINES);
2018                 qglVertex2f(x1, y1);
2019                 qglVertex2f(x2, y2);
2020                 qglEnd();
2021                 CHECKGLERROR
2022 #endif
2023                 break;
2024         case RENDERPATH_D3D9:
2025                 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2026                 break;
2027         case RENDERPATH_D3D10:
2028                 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2029                 break;
2030         case RENDERPATH_D3D11:
2031                 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2032                 break;
2033         case RENDERPATH_SOFT:
2034                 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2035                 break;
2036         case RENDERPATH_GLES1:
2037         case RENDERPATH_GLES2:
2038                 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2039                 return;
2040         }
2041 }
2042
2043 void DrawQ_Lines (float width, int numlines, const float *vertex3f, const float *color4f, int flags)
2044 {
2045         int i;
2046         qboolean hasalpha = false;
2047         for (i = 0;i < numlines*2;i++)
2048                 if (color4f[i*4+3] < 1.0f)
2049                         hasalpha = true;
2050
2051         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, hasalpha ? 0.5f : 1.0f);
2052
2053         if(!r_draw2d.integer && !r_draw2d_force)
2054                 return;
2055
2056         switch(vid.renderpath)
2057         {
2058         case RENDERPATH_GL11:
2059         case RENDERPATH_GL13:
2060         case RENDERPATH_GL20:
2061                 CHECKGLERROR
2062
2063                 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
2064
2065                 //qglLineWidth(width);CHECKGLERROR
2066
2067                 CHECKGLERROR
2068                 R_Mesh_PrepareVertices_Generic_Arrays(numlines*2, vertex3f, color4f, NULL);
2069                 qglDrawArrays(GL_LINES, 0, numlines*2);
2070                 CHECKGLERROR
2071                 break;
2072         case RENDERPATH_D3D9:
2073                 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2074                 break;
2075         case RENDERPATH_D3D10:
2076                 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2077                 break;
2078         case RENDERPATH_D3D11:
2079                 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2080                 break;
2081         case RENDERPATH_SOFT:
2082                 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2083                 break;
2084         case RENDERPATH_GLES1:
2085         case RENDERPATH_GLES2:
2086                 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2087                 return;
2088         }
2089 }
2090
2091 void DrawQ_SetClipArea(float x, float y, float width, float height)
2092 {
2093         int ix, iy, iw, ih;
2094         _DrawQ_Setup();
2095
2096         // We have to convert the con coords into real coords
2097         // OGL uses top to bottom
2098         ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
2099         iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
2100         iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
2101         ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
2102         switch(vid.renderpath)
2103         {
2104         case RENDERPATH_GL11:
2105         case RENDERPATH_GL13:
2106         case RENDERPATH_GL20:
2107         case RENDERPATH_GLES1:
2108         case RENDERPATH_GLES2:
2109         case RENDERPATH_SOFT:
2110                 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
2111                 break;
2112         case RENDERPATH_D3D9:
2113                 GL_Scissor(ix, iy, iw, ih);
2114                 break;
2115         case RENDERPATH_D3D10:
2116                 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2117                 break;
2118         case RENDERPATH_D3D11:
2119                 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2120                 break;
2121         }
2122
2123         GL_ScissorTest(true);
2124 }
2125
2126 void DrawQ_ResetClipArea(void)
2127 {
2128         _DrawQ_Setup();
2129         GL_ScissorTest(false);
2130 }
2131
2132 void DrawQ_Finish(void)
2133 {
2134         r_refdef.draw2dstage = 0;
2135 }
2136
2137 void DrawQ_RecalcView(void)
2138 {
2139         if(r_refdef.draw2dstage)
2140                 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2141 }
2142
2143 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2144 void R_DrawGamma(void)
2145 {
2146         float c[4];
2147         switch(vid.renderpath)
2148         {
2149         case RENDERPATH_GL20:
2150         case RENDERPATH_D3D9:
2151         case RENDERPATH_D3D10:
2152         case RENDERPATH_D3D11:
2153         case RENDERPATH_GLES2:
2154                 if (vid_usinghwgamma || v_glslgamma.integer)
2155                         return;
2156                 break;
2157         case RENDERPATH_GL11:
2158         case RENDERPATH_GL13:
2159                 if (vid_usinghwgamma)
2160                         return;
2161                 break;
2162         case RENDERPATH_GLES1:
2163         case RENDERPATH_SOFT:
2164                 return;
2165         }
2166         // all the blends ignore depth
2167 //      R_Mesh_ResetTextureState();
2168         R_SetupShader_Generic_NoTexture(true, true);
2169         GL_DepthMask(true);
2170         GL_DepthRange(0, 1);
2171         GL_PolygonOffset(0, 0);
2172         GL_DepthTest(false);
2173
2174         // interpretation of brightness and contrast:
2175         //   color range := brightness .. (brightness + contrast)
2176         // i.e. "c *= contrast; c += brightness"
2177         // plausible values for brightness thus range from -contrast to 1
2178
2179         // apply pre-brightness (subtractive brightness, for where contrast was >= 1)
2180         if (vid.support.ext_blend_subtract)
2181         {
2182                 if (v_color_enable.integer)
2183                 {
2184                         c[0] = -v_color_black_r.value / v_color_white_r.value;
2185                         c[1] = -v_color_black_g.value / v_color_white_g.value;
2186                         c[2] = -v_color_black_b.value / v_color_white_b.value;
2187                 }
2188                 else
2189                         c[0] = c[1] = c[2] = -v_brightness.value / v_contrast.value;
2190                 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2191                 {
2192                         // need SUBTRACTIVE blending to do this!
2193                         GL_BlendEquationSubtract(true);
2194                         GL_BlendFunc(GL_ONE, GL_ONE);
2195                         GL_Color(c[0], c[1], c[2], 1);
2196                         R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2197                         R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2198                         GL_BlendEquationSubtract(false);
2199                 }
2200         }
2201
2202         // apply contrast
2203         if (v_color_enable.integer)
2204         {
2205                 c[0] = v_color_white_r.value;
2206                 c[1] = v_color_white_g.value;
2207                 c[2] = v_color_white_b.value;
2208         }
2209         else
2210                 c[0] = c[1] = c[2] = v_contrast.value;
2211         if (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2212         {
2213                 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2214                 while (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2215                 {
2216                         float cc[4];
2217                         cc[0] = bound(0, c[0] - 1, 1);
2218                         cc[1] = bound(0, c[1] - 1, 1);
2219                         cc[2] = bound(0, c[2] - 1, 1);
2220                         GL_Color(cc[0], cc[1], cc[2], 1);
2221                         R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2222                         R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2223                         c[0] /= 1 + cc[0];
2224                         c[1] /= 1 + cc[1];
2225                         c[2] /= 1 + cc[2];
2226                 }
2227         }
2228         if (c[0] <= 0.997f || c[1] <= 0.997f || c[2] <= 0.997f)
2229         {
2230                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
2231                 GL_Color(c[0], c[1], c[2], 1);
2232                 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2233                 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2234         }
2235
2236         // apply post-brightness (additive brightness, for where contrast was <= 1)
2237         if (v_color_enable.integer)
2238         {
2239                 c[0] = v_color_black_r.value;
2240                 c[1] = v_color_black_g.value;
2241                 c[2] = v_color_black_b.value;
2242         }
2243         else
2244                 c[0] = c[1] = c[2] = v_brightness.value;
2245         if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2246         {
2247                 GL_BlendFunc(GL_ONE, GL_ONE);
2248                 GL_Color(c[0], c[1], c[2], 1);
2249                 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2250                 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2251         }
2252 }
2253