2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
28 #include "ft2_fontdefs.h"
34 // this flag indicates that it should be loaded and unloaded on demand
36 // texture flags to upload with
38 // texture may be freed after a while
41 skinframe_t *skinframe;
42 // used for hash lookups
43 struct cachepic_s *chain;
44 // flags - CACHEPICFLAG_NEWPIC for example
51 static mempool_t *fonts_mempool = NULL;
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)"};
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"};
67 //=============================================================================
68 /* Support Routines */
70 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
71 static cachepic_t cachepics[MAX_CACHED_PICS];
72 static int numcachepics;
74 rtexturepool_t *drawtexturepool;
83 // FIXME: move this to client somehow
84 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
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;
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)
105 if (!strcmp(path, pic->name))
107 // if it was created (or replaced) by Draw_NewPic, just return it
108 if (pic->flags & CACHEPICFLAG_NEWPIC)
111 R_SkinFrame_MarkUsed(pic->skinframe);
112 pic->lastusedframe = draw_frame;
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
117 if (!pic->skinframe || !pic->skinframe->base)
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;
128 if (numcachepics == MAX_CACHED_PICS)
130 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
131 // FIXME: support NULL in callers?
132 return cachepics; // return the first one
134 pic = cachepics + (numcachepics++);
135 memset(pic, 0, sizeof(*pic));
136 strlcpy (pic->name, path, sizeof(pic->name));
138 pic->chain = cachepichash[hashkey];
139 cachepichash[hashkey] = pic;
143 R_SkinFrame_PurgeSkinFrame(pic->skinframe);
145 pic->flags = cachepicflags;
146 pic->texflags = texflags;
147 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT) != 0;
148 pic->lastusedframe = draw_frame;
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);
153 // get the dimensions of the image we loaded (if it was successful)
154 if (pic->skinframe && pic->skinframe->base)
156 pic->width = R_TextureWidth(pic->skinframe->base);
157 pic->height = R_TextureHeight(pic->skinframe->base);
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);
166 cachepic_t *Draw_CachePic (const char *path)
168 return Draw_CachePic_Flags (path, 0); // default to persistent!
171 const char *Draw_GetPicName(cachepic_t *pic)
178 int Draw_GetPicWidth(cachepic_t *pic)
185 int Draw_GetPicHeight(cachepic_t *pic)
192 qboolean Draw_IsPicLoaded(cachepic_t *pic)
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;
202 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
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;
212 void Draw_Frame(void)
216 static double nextpurgetime;
217 if (nextpurgetime > realtime)
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);
226 cachepic_t *Draw_NewPic(const char *picname, int width, int height, unsigned char *pixels_bgra, textype_t textype, int texflags)
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))
239 if (pic->flags & CACHEPICFLAG_NEWPIC && pic->skinframe && pic->skinframe->base && pic->width == width && pic->height == height)
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;
249 if (numcachepics == MAX_CACHED_PICS)
251 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
252 // FIXME: support NULL in callers?
253 return cachepics; // return the first one
255 pic = cachepics + (numcachepics++);
256 memset(pic, 0, sizeof(*pic));
257 strlcpy (pic->name, picname, sizeof(pic->name));
259 pic->chain = cachepichash[hashkey];
260 cachepichash[hashkey] = pic;
263 R_SkinFrame_PurgeSkinFrame(pic->skinframe);
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;
269 pic->height = height;
270 pic->skinframe = R_SkinFrame_LoadInternalBGRA(picname, texflags, pixels_bgra, width, height, vid.sRGB2D);
271 pic->lastusedframe = draw_frame;
275 void Draw_FreePic(const char *picname)
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)
285 if (!strcmp (picname, pic->name) && pic->skinframe)
287 R_SkinFrame_PurgeSkinFrame(pic->skinframe);
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)
301 char widthfile[MAX_QPATH];
303 fs_offset_t widthbufsize;
305 if(override || !fnt->texpath[0])
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;
320 if (fnt->settings.scale <= 0)
321 fnt->settings.scale = 1;
323 if(drawtexturepool == NULL)
324 return; // before gl_draw_start, so will be loaded later
328 // clear freetype font
329 Font_UnloadFont(fnt->ft2);
334 if(fnt->req_face != -1)
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);
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))
343 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
345 if (!fnt->fallbacks[i][0])
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))
351 if(!Draw_IsPicLoaded(fnt->pic))
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));
357 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
360 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
362 // unspecified width == 1 (base width)
363 for(ch = 0; ch < 256; ++ch)
364 fnt->width_of[ch] = 1;
366 // FIXME load "name.width", if it fails, fill all with 1
367 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
369 float extraspacing = 0;
370 const char *p = widthbuf;
375 if(!COM_ParseToken_Simple(&p, false, false, true))
393 fnt->width_of[ch] = atof(com_token) + extraspacing;
397 if(!strcmp(com_token, "extraspacing"))
399 if(!COM_ParseToken_Simple(&p, false, false, true))
401 extraspacing = atof(com_token);
403 else if(!strcmp(com_token, "scale"))
405 if(!COM_ParseToken_Simple(&p, false, false, true))
407 fnt->settings.scale = atof(com_token);
411 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
412 if(!COM_ParseToken_Simple(&p, false, false, true))
424 for (i = 0; i < MAX_FONT_SIZES; ++i)
426 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
429 for(ch = 0; ch < 256; ++ch)
430 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
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;
439 // fix up maxwidth for overlap
440 fnt->maxwidth *= fnt->settings.scale;
442 if(fnt == FONT_CONSOLE)
443 con_linewidth = -1; // rewrap console in next frame
446 extern cvar_t developer_font;
447 dp_font_t *FindFont(const char *title, qboolean allocate_new)
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
458 // find any font with empty title
459 for(i = 0; i < dp_fonts.maxsize; ++i)
461 if(!strcmp(dp_fonts.f[i].title, ""))
463 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
464 return &dp_fonts.f[i];
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];
484 static float snap_to_pixel_x(float x, float roundUpAt)
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);
491 x = (int)(x * vid.width / vid_conwidth.value);
492 x = (x * vid_conwidth.value / vid.width);
497 static float snap_to_pixel_y(float y, float roundUpAt)
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);
504 y = (int)(y * vid.height / vid_conheight.value);
505 y = (y * vid_conheight.value / vid.height);
510 static void LoadFont_f(void)
514 const char *filelist, *c, *cm;
515 float sz, scale, voffset;
516 char mainfont[MAX_QPATH];
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"
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"
538 f = FindFont(Cmd_Argv(1), true);
541 Con_Printf("font function not found\n");
546 filelist = "gfx/conchars";
548 filelist = Cmd_Argv(2);
550 memset(f->fallbacks, 0, sizeof(f->fallbacks));
551 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
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);
564 if(!c || (c - filelist) > MAX_QPATH)
565 strlcpy(mainfont, filelist, sizeof(mainfont));
568 memcpy(mainfont, filelist, c - filelist);
569 mainfont[c - filelist] = 0;
572 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
574 c = strchr(filelist, ',');
580 c = strchr(filelist, ':');
581 cm = strchr(filelist, ',');
582 if(c && (!cm || c < cm))
583 f->fallback_faces[i] = atoi(c+1);
586 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
589 if(!c || (c-filelist) > MAX_QPATH)
591 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
595 memcpy(f->fallbacks[i], filelist, c - filelist);
596 f->fallbacks[i][c - filelist] = 0;
600 // for now: by default load only one size: the default size
602 for(i = 1; i < MAX_FONT_SIZES; ++i)
603 f->req_sizes[i] = -1;
609 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
612 if (!strcmp(Cmd_Argv(i), "scale"))
616 scale = atof(Cmd_Argv(i));
619 if (!strcmp(Cmd_Argv(i), "voffset"))
623 voffset = atof(Cmd_Argv(i));
628 continue; // no slot for other sizes
630 // parse one of sizes
631 sz = atof(Cmd_Argv(i));
632 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
634 // search for duplicated sizes
636 for (j=0; j<sizes; j++)
637 if (f->req_sizes[j] == sz)
640 continue; // sz already in req_sizes, don't add it again
642 if (sizes == MAX_FONT_SIZES)
644 Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
648 f->req_sizes[sizes] = sz;
654 LoadFont(true, mainfont, f, scale, voffset);
662 static void gl_draw_start(void)
666 drawtexturepool = R_AllocTexturePool();
669 memset(cachepichash, 0, sizeof(cachepichash));
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);
678 // draw the loading screen so people have something to see in the newly opened window
679 SCR_UpdateLoadingScreen(true, true);
682 static void gl_draw_shutdown(void)
686 R_FreeTexturePool(&drawtexturepool);
689 memset(cachepichash, 0, sizeof(cachepichash));
692 static void gl_draw_newmap(void)
697 // mark all of the persistent pics so they are not purged...
698 for (i = 0; i < numcachepics; i++)
700 cachepic_t *pic = cachepics + i;
701 if (!pic->autoload && pic->skinframe)
702 R_SkinFrame_MarkUsed(pic->skinframe);
706 void GL_Draw_Init (void)
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);
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);
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++);
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);
747 static void _DrawQ_Setup(void) // see R_ResetViewRendering2D
749 if (r_refdef.draw2dstage == 1)
752 r_refdef.draw2dstage = 1;
753 R_ResetViewRendering2D_Common(0, NULL, NULL, vid_conwidth.integer, vid_conheight.integer);
756 qboolean r_draw2d_force = false;
758 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
760 dp_model_t *mod = CL_Mesh_UI();
765 pic = Draw_CachePic("white");
769 height = pic->height;
770 surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, pic->name, flags, pic->texflags, MATERIALFLAG_VERTEXCOLOR), true);
771 e0 = Mod_Mesh_IndexForVertex(mod, surf, x , y , 0, 0, 0, -1, 0, 0, 0, 0, red, green, blue, alpha);
772 e1 = Mod_Mesh_IndexForVertex(mod, surf, x + width, y , 0, 0, 0, -1, 1, 0, 0, 0, red, green, blue, alpha);
773 e2 = Mod_Mesh_IndexForVertex(mod, surf, x + width, y + height, 0, 0, 0, -1, 1, 1, 0, 0, red, green, blue, alpha);
774 e3 = Mod_Mesh_IndexForVertex(mod, surf, x , y + height, 0, 0, 0, -1, 0, 1, 0, 0, red, green, blue, alpha);
775 Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
776 Mod_Mesh_AddTriangle(mod, surf, e0, e2, e3);
779 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)
781 float af = DEG2RAD(-angle); // forward
782 float ar = DEG2RAD(-angle + 90); // right
783 float sinaf = sin(af);
784 float cosaf = cos(af);
785 float sinar = sin(ar);
786 float cosar = cos(ar);
787 dp_model_t *mod = CL_Mesh_UI();
792 pic = Draw_CachePic("white");
796 height = pic->height;
797 surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, pic->name, flags, pic->texflags, MATERIALFLAG_VERTEXCOLOR), true);
798 e0 = Mod_Mesh_IndexForVertex(mod, surf, x - cosaf * org_x - cosar * org_y, y - sinaf * org_x - sinar * org_y, 0, 0, 0, -1, 0, 0, 0, 0, red, green, blue, alpha);
799 e1 = Mod_Mesh_IndexForVertex(mod, surf, x + cosaf * (width - org_x) - cosar * org_y, y + sinaf * (width - org_x) - sinar * org_y, 0, 0, 0, -1, 1, 0, 0, 0, red, green, blue, alpha);
800 e2 = Mod_Mesh_IndexForVertex(mod, surf, x + cosaf * (width - org_x) + cosar * (height - org_y), sinaf*(width - org_x) + sinar * (height - org_y), 0, 0, 0, -1, 1, 1, 0, 0, red, green, blue, alpha);
801 e3 = Mod_Mesh_IndexForVertex(mod, surf, x - cosaf * org_x + cosar * (height - org_y), y - sinaf * org_x + sinar * (height - org_y), 0, 0, 0, -1, 0, 1, 0, 0, red, green, blue, alpha);
802 Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
803 Mod_Mesh_AddTriangle(mod, surf, e0, e2, e3);
806 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
808 DrawQ_Pic(x, y, Draw_CachePic("white"), width, height, red, green, blue, alpha, flags);
811 /// color tag printing
812 static const vec4_t string_colors[] =
815 // LordHavoc: why on earth is cyan before magenta in Quake3?
816 // LordHavoc: note: Doom3 uses white for [0] and [7]
817 {0.0, 0.0, 0.0, 1.0}, // black
818 {1.0, 0.0, 0.0, 1.0}, // red
819 {0.0, 1.0, 0.0, 1.0}, // green
820 {1.0, 1.0, 0.0, 1.0}, // yellow
821 {0.0, 0.0, 1.0, 1.0}, // blue
822 {0.0, 1.0, 1.0, 1.0}, // cyan
823 {1.0, 0.0, 1.0, 1.0}, // magenta
824 {1.0, 1.0, 1.0, 1.0}, // white
825 // [515]'s BX_COLOREDTEXT extension
826 {1.0, 1.0, 1.0, 0.5}, // half transparent
827 {0.5, 0.5, 0.5, 1.0} // half brightness
828 // Black's color table
829 //{1.0, 1.0, 1.0, 1.0},
830 //{1.0, 0.0, 0.0, 1.0},
831 //{0.0, 1.0, 0.0, 1.0},
832 //{0.0, 0.0, 1.0, 1.0},
833 //{1.0, 1.0, 0.0, 1.0},
834 //{0.0, 1.0, 1.0, 1.0},
835 //{1.0, 0.0, 1.0, 1.0},
836 //{0.1, 0.1, 0.1, 1.0}
839 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
841 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
843 float C = r_textcontrast.value;
844 float B = r_textbrightness.value;
845 if (colorindex & 0x10000) // that bit means RGB color
847 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
848 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
849 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
850 color[3] = (colorindex & 0xf) / 15.0;
853 Vector4Copy(string_colors[colorindex], color);
854 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
857 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
858 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
862 // NOTE: this function always draws exactly one character if maxwidth <= 0
863 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)
865 const char *text_start = text;
866 int colorindex = STRING_COLOR_DEFAULT;
869 Uchar ch, mapch, nextch;
870 Uchar prevch = 0; // used for kerning
875 ft2_font_map_t *fontmap = NULL;
876 ft2_font_map_t *map = NULL;
877 //ft2_font_map_t *prevmap = NULL;
878 ft2_font_t *ft2 = fnt->ft2;
880 qboolean snap = true;
881 qboolean least_one = false;
882 float dw; // display w
883 //float dh; // display h
884 const float *width_of;
891 // do this in the end
892 w *= fnt->settings.scale;
893 h *= fnt->settings.scale;
895 // find the most fitting size:
899 map_index = Font_IndexForSize(ft2, h, &w, &h);
901 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
902 fontmap = Font_MapForIndex(ft2, map_index);
911 if (!outcolor || *outcolor == -1)
912 colorindex = STRING_COLOR_DEFAULT;
914 colorindex = *outcolor;
916 // maxwidth /= fnt->scale; // w and h are multiplied by it already
917 // ftbase_x = snap_to_pixel_x(0);
922 maxwidth = -maxwidth;
926 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
929 width_of = fontmap->width_of;
931 width_of = fnt->width_of;
934 while (((bytes_left = *maxlen - (text - text_start)) > 0) && *text)
937 nextch = ch = u8_getnchar(text, &text, bytes_left);
938 i = text - text_start;
941 if (ch == ' ' && !fontmap)
943 if(!least_one || i0) // never skip the first character
944 if(x + width_of[(int) ' '] * dw > maxwidth)
947 break; // oops, can't draw this
949 x += width_of[(int) ' '] * dw;
952 // i points to the char after ^
953 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
955 ch = *text; // colors are ascii, so no u8_ needed
956 if (ch <= '9' && ch >= '0') // ^[0-9] found
958 colorindex = ch - '0';
963 // i points to the char after ^...
964 // i+3 points to 3 in ^x123
965 // i+3 == *maxlen would mean that char is missing
966 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
968 // building colorindex...
969 ch = tolower(text[1]);
970 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
971 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
972 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
973 else tempcolorindex = 0;
976 ch = tolower(text[2]);
977 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
978 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
979 else tempcolorindex = 0;
982 ch = tolower(text[3]);
983 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
984 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
985 else tempcolorindex = 0;
988 colorindex = tempcolorindex | 0xf;
989 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
997 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1006 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1013 map = ft2_oldstyle_map;
1015 if(!least_one || i0) // never skip the first character
1016 if(x + width_of[ch] * dw > maxwidth)
1019 break; // oops, can't draw this
1021 x += width_of[ch] * dw;
1023 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1025 map = FontMap_FindForChar(fontmap, ch);
1028 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1034 mapch = ch - map->start;
1035 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1037 x += map->glyphs[mapch].advance_x * dw;
1046 *outcolor = colorindex;
1051 float DrawQ_Color[4];
1052 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)
1054 int shadow, colorindex = STRING_COLOR_DEFAULT;
1056 float x = startx, y, s, t, u, v, thisw;
1057 Uchar ch, mapch, nextch;
1058 Uchar prevch = 0; // used for kerning
1061 //ft2_font_map_t *prevmap = NULL; // the previous map
1062 ft2_font_map_t *map = NULL; // the currently used map
1063 ft2_font_map_t *fontmap = NULL; // the font map for the size
1065 const char *text_start = text;
1067 ft2_font_t *ft2 = fnt->ft2;
1068 qboolean snap = true;
1072 const float *width_of;
1073 dp_model_t *mod = CL_Mesh_UI();
1074 msurface_t *surf = NULL;
1077 tw = Draw_GetPicWidth(fnt->pic);
1078 th = Draw_GetPicHeight(fnt->pic);
1088 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1089 w *= fnt->settings.scale;
1090 h *= fnt->settings.scale;
1095 map_index = Font_IndexForSize(ft2, h, &w, &h);
1097 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1098 fontmap = Font_MapForIndex(ft2, map_index);
1104 // draw the font at its baseline when using freetype
1106 ftbase_y = dh * (4.5/6.0);
1111 if(!r_draw2d.integer && !r_draw2d_force)
1112 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1114 //ftbase_x = snap_to_pixel_x(ftbase_x);
1117 startx = snap_to_pixel_x(startx, 0.4);
1118 starty = snap_to_pixel_y(starty, 0.4);
1119 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1122 pix_x = vid.width / vid_conwidth.value;
1123 pix_y = vid.height / vid_conheight.value;
1126 width_of = fontmap->width_of;
1128 width_of = fnt->width_of;
1130 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1135 if (!outcolor || *outcolor == -1)
1136 colorindex = STRING_COLOR_DEFAULT;
1138 colorindex = *outcolor;
1140 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1147 x += r_textshadow.value * vid.width / vid_conwidth.value;
1148 y += r_textshadow.value * vid.height / vid_conheight.value;
1151 while (((bytes_left = maxlen - (text - text_start)) > 0) && *text)
1153 nextch = ch = u8_getnchar(text, &text, bytes_left);
1154 i = text - text_start;
1157 if (ch == ' ' && !fontmap)
1159 x += width_of[(int) ' '] * dw;
1162 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1164 ch = *text; // colors are ascii, so no u8_ needed
1165 if (ch <= '9' && ch >= '0') // ^[0-9] found
1167 colorindex = ch - '0';
1168 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1173 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1175 // building colorindex...
1176 ch = tolower(text[1]);
1177 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1178 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1179 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1180 else tempcolorindex = 0;
1183 ch = tolower(text[2]);
1184 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1185 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1186 else tempcolorindex = 0;
1189 ch = tolower(text[3]);
1190 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1191 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1192 else tempcolorindex = 0;
1195 colorindex = tempcolorindex | 0xf;
1196 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1197 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1198 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1206 else if (ch == STRING_COLOR_TAG)
1215 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1216 // this way we don't need to rebind fnt->tex for every old-style character
1217 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1220 x += 1.0/pix_x * r_textshadow.value;
1221 y += 1.0/pix_y * r_textshadow.value;
1223 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1230 map = ft2_oldstyle_map;
1232 //num = (unsigned char) text[i];
1233 //thisw = fnt->width_of[num];
1234 thisw = fnt->width_of[ch];
1235 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1236 if (r_nearest_conchars.integer)
1238 s = (ch & 15)*0.0625f;
1239 t = (ch >> 4)*0.0625f;
1240 u = 0.0625f * thisw;
1245 s = (ch & 15)*0.0625f + (0.5f / tw);
1246 t = (ch >> 4)*0.0625f + (0.5f / th);
1247 u = 0.0625f * thisw - (1.0f / tw);
1248 v = 0.0625f - (1.0f / th);
1250 surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, fnt->pic->name, flags, TEXF_ALPHA | TEXF_CLAMP, MATERIALFLAG_VERTEXCOLOR), true);
1251 e0 = Mod_Mesh_IndexForVertex(mod, surf, x , y , 10, 0, 0, -1, s , t , 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1252 e1 = Mod_Mesh_IndexForVertex(mod, surf, x+dw*thisw, y , 10, 0, 0, -1, s+u, t , 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1253 e2 = Mod_Mesh_IndexForVertex(mod, surf, x+dw*thisw, y+dh, 10, 0, 0, -1, s+u, t+v, 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1254 e3 = Mod_Mesh_IndexForVertex(mod, surf, x , y+dh, 10, 0, 0, -1, s , t+v, 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1255 Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
1256 Mod_Mesh_AddTriangle(mod, surf, e0, e2, e3);
1257 x += width_of[ch] * dw;
1259 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1262 map = FontMap_FindForChar(fontmap, ch);
1265 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1272 // this shouldn't happen
1279 mapch = ch - map->start;
1280 thisw = map->glyphs[mapch].advance_x;
1284 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1291 surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, map->pic->name, flags, TEXF_ALPHA | TEXF_CLAMP, MATERIALFLAG_VERTEXCOLOR), true);
1292 e0 = Mod_Mesh_IndexForVertex(mod, surf, x + dw * map->glyphs[mapch].vxmin, y + dh * map->glyphs[mapch].vymin, 10, 0, 0, -1, map->glyphs[mapch].txmin, map->glyphs[mapch].tymin, 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1293 e1 = Mod_Mesh_IndexForVertex(mod, surf, x + dw * map->glyphs[mapch].vxmax, y + dh * map->glyphs[mapch].vymin, 10, 0, 0, -1, map->glyphs[mapch].txmax, map->glyphs[mapch].tymin, 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1294 e2 = Mod_Mesh_IndexForVertex(mod, surf, x + dw * map->glyphs[mapch].vxmax, y + dh * map->glyphs[mapch].vymax, 10, 0, 0, -1, map->glyphs[mapch].txmax, map->glyphs[mapch].tymax, 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1295 e3 = Mod_Mesh_IndexForVertex(mod, surf, x + dw * map->glyphs[mapch].vxmin, y + dh * map->glyphs[mapch].vymax, 10, 0, 0, -1, map->glyphs[mapch].txmin, map->glyphs[mapch].tymax, 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1296 Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
1297 Mod_Mesh_AddTriangle(mod, surf, e0, e2, e3);
1309 x -= 1.0/pix_x * r_textshadow.value;
1310 y -= 1.0/pix_y * r_textshadow.value;
1316 *outcolor = colorindex;
1318 // note: this relies on the proper text (not shadow) being drawn last
1322 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)
1324 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1327 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)
1329 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1332 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1334 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1337 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1339 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1344 // no ^xrgb management
1345 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1347 int color, numchars = 0;
1348 char *outputend2c = output2c + maxoutchars - 2;
1349 if (!outcolor || *outcolor == -1)
1350 color = STRING_COLOR_DEFAULT;
1354 maxreadchars = 1<<30;
1355 textend = text + maxreadchars;
1356 while (text != textend && *text)
1358 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1360 if (text[1] == STRING_COLOR_TAG)
1362 else if (text[1] >= '0' && text[1] <= '9')
1364 color = text[1] - '0';
1369 if (output2c >= outputend2c)
1371 *output2c++ = *text++;
1372 *output2c++ = color;
1375 output2c[0] = output2c[1] = 0;
1382 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)
1384 dp_model_t *mod = CL_Mesh_UI();
1389 pic = Draw_CachePic("white");
1393 height = pic->height;
1394 surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, pic->name, flags, pic->texflags, MATERIALFLAG_VERTEXCOLOR), true);
1395 e0 = Mod_Mesh_IndexForVertex(mod, surf, x , y , 0, 0, 0, -1, s1, t1, 0, 0, r1, g1, b1, a1);
1396 e1 = Mod_Mesh_IndexForVertex(mod, surf, x + width, y , 0, 0, 0, -1, s2, t2, 0, 0, r2, g2, b2, a2);
1397 e2 = Mod_Mesh_IndexForVertex(mod, surf, x + width, y + height, 0, 0, 0, -1, s4, t4, 0, 0, r4, g4, b4, a4);
1398 e3 = Mod_Mesh_IndexForVertex(mod, surf, x , y + height, 0, 0, 0, -1, s3, t3, 0, 0, r3, g3, b3, a3);
1399 Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
1400 Mod_Mesh_AddTriangle(mod, surf, e0, e2, e3);
1403 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1405 dp_model_t *mod = CL_Mesh_UI();
1408 float offsetx, offsety;
1410 // width is measured in real pixels
1411 if (fabs(x2 - x1) > fabs(y2 - y1))
1414 offsety = width * vid_conheight.value / vid.height;
1418 offsetx = width * vid_conwidth.value / vid.width;
1421 surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, "white", 0, 0, MATERIALFLAG_VERTEXCOLOR), true);
1422 e0 = Mod_Mesh_IndexForVertex(mod, surf, x1 - offsetx, y1 - offsety, 10, 0, 0, -1, 0, 0, 0, 0, r, g, b, alpha);
1423 e1 = Mod_Mesh_IndexForVertex(mod, surf, x2 - offsetx, y2 - offsety, 10, 0, 0, -1, 0, 0, 0, 0, r, g, b, alpha);
1424 e2 = Mod_Mesh_IndexForVertex(mod, surf, x2 + offsetx, y2 + offsety, 10, 0, 0, -1, 0, 0, 0, 0, r, g, b, alpha);
1425 e3 = Mod_Mesh_IndexForVertex(mod, surf, x1 + offsetx, y1 + offsety, 10, 0, 0, -1, 0, 0, 0, 0, r, g, b, alpha);
1426 Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
1427 Mod_Mesh_AddTriangle(mod, surf, e0, e2, e3);
1430 void DrawQ_SetClipArea(float x, float y, float width, float height)
1436 // We have to convert the con coords into real coords
1437 // OGL uses bottom to top (origin is in bottom left)
1438 ix = (int)(0.5 + x * ((float)r_refdef.view.width / vid_conwidth.integer)) + r_refdef.view.x;
1439 iy = (int)(0.5 + y * ((float)r_refdef.view.height / vid_conheight.integer)) + r_refdef.view.y;
1440 iw = (int)(0.5 + width * ((float)r_refdef.view.width / vid_conwidth.integer));
1441 ih = (int)(0.5 + height * ((float)r_refdef.view.height / vid_conheight.integer));
1442 switch(vid.renderpath)
1444 case RENDERPATH_GL11:
1445 case RENDERPATH_GL13:
1446 case RENDERPATH_GL20:
1447 case RENDERPATH_GLES1:
1448 case RENDERPATH_GLES2:
1449 case RENDERPATH_SOFT:
1450 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1452 case RENDERPATH_D3D9:
1453 GL_Scissor(ix, iy, iw, ih);
1455 case RENDERPATH_D3D10:
1456 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1458 case RENDERPATH_D3D11:
1459 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1463 GL_ScissorTest(true);
1466 void DrawQ_ResetClipArea(void)
1470 GL_ScissorTest(false);
1473 void DrawQ_Finish(void)
1476 r_refdef.draw2dstage = 0;
1479 void DrawQ_RecalcView(void)
1482 if(r_refdef.draw2dstage)
1483 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
1486 void DrawQ_FlushUI(void)
1489 dp_model_t *mod = CL_Mesh_UI();
1490 if (mod->num_surfaces == 0)
1493 if (!r_draw2d.integer && !r_draw2d_force)
1495 Mod_Mesh_Reset(mod);
1499 // TODO: render the mesh using R_Q1BSP_Draw or similar, for full material support.
1500 GL_DepthMask(false);
1501 R_Mesh_PrepareVertices_Generic_Arrays(mod->surfmesh.num_vertices, mod->surfmesh.data_vertex3f, mod->surfmesh.data_lightmapcolor4f, mod->surfmesh.data_texcoordtexture2f);
1502 for (i = 0; i < mod->num_surfaces; i++)
1504 msurface_t *surf = mod->data_surfaces + i;
1505 texture_t *tex = surf->texture;
1506 if (tex->currentmaterialflags & MATERIALFLAG_CUSTOMBLEND)
1507 GL_BlendFunc(tex->customblendfunc[0], tex->customblendfunc[1]);
1508 else if (tex->currentmaterialflags & MATERIALFLAG_ADD)
1509 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1510 else if (tex->currentmaterialflags & MATERIALFLAG_ALPHA)
1511 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1513 GL_BlendFunc(GL_ONE, GL_ZERO);
1514 R_SetupShader_Generic(tex->currentskinframe->base, NULL, GL_MODULATE, 1, (tex->currentmaterialflags & MATERIALFLAG_CUSTOMBLEND) ? false : true, true, false);
1515 R_Mesh_Draw(surf->num_firstvertex, surf->num_vertices, surf->num_firsttriangle, surf->num_triangles, mod->surfmesh.data_element3i, NULL, 0, mod->surfmesh.data_element3s, NULL, 0);
1518 Mod_Mesh_Reset(mod);