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