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.
26 #include "cl_dyntexture.h"
29 #include "ft2_fontdefs.h"
32 static mempool_t *fonts_mempool = NULL;
34 cvar_t r_textshadow = {CVAR_SAVE, "r_textshadow", "0", "draws a shadow on all text to improve readability (note: value controls offset, 1 = 1 pixel, 1.5 = 1.5 pixels, etc)"};
35 cvar_t r_textbrightness = {CVAR_SAVE, "r_textbrightness", "0", "additional brightness for text color codes (0 keeps colors as is, 1 makes them all white)"};
36 cvar_t r_textcontrast = {CVAR_SAVE, "r_textcontrast", "1", "additional contrast for text color codes (1 keeps colors as is, 0 makes them all black)"};
38 cvar_t r_font_postprocess_blur = {CVAR_SAVE, "r_font_postprocess_blur", "0", "font blur amount"};
39 cvar_t r_font_postprocess_outline = {CVAR_SAVE, "r_font_postprocess_outline", "0", "font outline amount"};
40 cvar_t r_font_postprocess_shadow_x = {CVAR_SAVE, "r_font_postprocess_shadow_x", "0", "font shadow X shift amount, applied during outlining"};
41 cvar_t r_font_postprocess_shadow_y = {CVAR_SAVE, "r_font_postprocess_shadow_y", "0", "font shadow Y shift amount, applied during outlining"};
42 cvar_t r_font_postprocess_shadow_z = {CVAR_SAVE, "r_font_postprocess_shadow_z", "0", "font shadow Z shift amount, applied during blurring"};
43 cvar_t r_font_hinting = {CVAR_SAVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"};
44 cvar_t r_font_antialias = {CVAR_SAVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */};
46 extern cvar_t v_glslgamma;
48 //=============================================================================
49 /* Support Routines */
51 #define FONT_FILESIZE 13468
52 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
53 static cachepic_t cachepics[MAX_CACHED_PICS];
54 static int numcachepics;
56 static rtexturepool_t *drawtexturepool;
58 static const unsigned char concharimage[FONT_FILESIZE] =
63 static rtexture_t *draw_generateconchars(void)
70 data = LoadTGA_BGRA (concharimage, FONT_FILESIZE, NULL);
72 for (i = 0;i < 8192;i++)
74 random = lhrandom (0.0,1.0);
75 data[i*4+3] = data[i*4+0];
76 data[i*4+2] = 83 + (unsigned char)(random * 64);
77 data[i*4+1] = 71 + (unsigned char)(random * 32);
78 data[i*4+0] = 23 + (unsigned char)(random * 16);
81 for (i = 8192;i < 32768;i++)
83 random = lhrandom (0.0,1.0);
84 data[i*4+3] = data[i*4+0];
85 data[i*4+2] = 95 + (unsigned char)(random * 64);
86 data[i*4+1] = 95 + (unsigned char)(random * 64);
87 data[i*4+0] = 95 + (unsigned char)(random * 64);
90 for (i = 32768;i < 40960;i++)
92 random = lhrandom (0.0,1.0);
93 data[i*4+3] = data[i*4+0];
94 data[i*4+2] = 83 + (unsigned char)(random * 64);
95 data[i*4+1] = 71 + (unsigned char)(random * 32);
96 data[i*4+0] = 23 + (unsigned char)(random * 16);
99 for (i = 40960;i < 65536;i++)
101 random = lhrandom (0.0,1.0);
102 data[i*4+3] = data[i*4+0];
103 data[i*4+2] = 96 + (unsigned char)(random * 64);
104 data[i*4+1] = 43 + (unsigned char)(random * 32);
105 data[i*4+0] = 27 + (unsigned char)(random * 32);
109 Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, data);
112 tex = R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, data, TEXTYPE_BGRA, TEXF_ALPHA, -1, NULL);
117 static rtexture_t *draw_generateditherpattern(void)
120 unsigned char pixels[8][8];
121 for (y = 0;y < 8;y++)
122 for (x = 0;x < 8;x++)
123 pixels[y][x] = ((x^y) & 4) ? 254 : 0;
124 return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST, -1, palette_bgra_transparent);
127 typedef struct embeddedpic_s
136 static const embeddedpic_t embeddedpics[] =
139 "gfx/prydoncursor001", 16, 16,
158 "ui/mousepointer", 16, 16,
177 "gfx/crosshair1", 16, 16,
196 "gfx/crosshair2", 16, 16,
215 "gfx/crosshair3", 16, 16,
234 "gfx/crosshair4", 16, 16,
253 "gfx/crosshair5", 8, 8,
264 "gfx/crosshair6", 2, 2,
269 "gfx/crosshair7", 16, 16,
290 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
292 const embeddedpic_t *p;
293 for (p = embeddedpics;p->name;p++)
294 if (!strcmp(name, p->name))
295 return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA, -1, palette_bgra_embeddedpic);
296 if (!strcmp(name, "gfx/conchars"))
297 return draw_generateconchars();
298 if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
299 return draw_generateditherpattern();
301 Con_DPrintf("Draw_CachePic: failed to load %s\n", name);
302 return r_texture_notexture;
311 // FIXME: move this to client somehow
312 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
315 unsigned char *pixels;
318 unsigned char *lmpdata;
319 char lmpname[MAX_QPATH];
322 texflags = TEXF_ALPHA;
323 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
324 texflags |= TEXF_CLAMP;
325 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer)
326 texflags |= TEXF_COMPRESS;
328 // check whether the picture has already been cached
329 crc = CRC_Block((unsigned char *)path, strlen(path));
330 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
331 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
332 if (!strcmp (path, pic->name))
333 if(pic->texflags == texflags)
336 if (numcachepics == MAX_CACHED_PICS)
338 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
339 // FIXME: support NULL in callers?
340 return cachepics; // return the first one
342 pic = cachepics + (numcachepics++);
343 strlcpy (pic->name, path, sizeof(pic->name));
345 pic->chain = cachepichash[hashkey];
346 cachepichash[hashkey] = pic;
348 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
349 pic->tex = CL_GetDynTexture( path );
350 // if so, set the width/height, too
352 pic->width = R_TextureWidth(pic->tex);
353 pic->height = R_TextureHeight(pic->tex);
354 // we're done now (early-out)
358 pic->texflags = texflags;
359 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
361 // load a high quality image from disk if possible
362 pixels = loadimagepixelsbgra(path, false, true, r_texture_convertsRGB_2d.integer != 0, NULL);
363 if (pixels == NULL && !strncmp(path, "gfx/", 4))
364 pixels = loadimagepixelsbgra(path+4, false, true, r_texture_convertsRGB_2d.integer != 0, NULL);
367 pic->width = image_width;
368 pic->height = image_height;
370 pic->tex = R_LoadTexture2D(drawtexturepool, path, image_width, image_height, pixels, TEXTYPE_BGRA, pic->texflags, -1, NULL);
374 pic->autoload = false;
375 // never compress the fallback images
376 pic->texflags &= ~TEXF_COMPRESS;
379 // now read the low quality version (wad or lmp file), and take the pic
380 // size from that even if we don't upload the texture, this way the pics
381 // show up the right size in the menu even if they were replaced with
382 // higher or lower resolution versions
383 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
384 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
386 if (developer_loading.integer)
387 Con_Printf("loading lump \"%s\"\n", path);
391 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
392 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
393 // if no high quality replacement image was found, upload the original low quality texture
395 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
399 else if ((lmpdata = W_GetLumpName (path + 4)))
401 if (developer_loading.integer)
402 Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
404 if (!strcmp(path, "gfx/conchars"))
406 // conchars is a raw image and with color 0 as transparent instead of 255
409 // if no high quality replacement image was found, upload the original low quality texture
411 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
415 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
416 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
417 // if no high quality replacement image was found, upload the original low quality texture
419 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
428 else if (pic->tex == NULL)
430 // if it's not found on disk, generate an image
431 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
432 pic->width = R_TextureWidth(pic->tex);
433 pic->height = R_TextureHeight(pic->tex);
439 cachepic_t *Draw_CachePic (const char *path)
441 return Draw_CachePic_Flags (path, 0);
446 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
448 if (pic->autoload && !pic->tex)
450 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, r_texture_convertsRGB_2d.integer != 0);
451 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
452 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, r_texture_convertsRGB_2d.integer != 0);
453 if (pic->tex == NULL)
454 pic->tex = draw_generatepic(pic->name, true);
456 pic->lastusedframe = draw_frame;
460 void Draw_Frame(void)
464 static double nextpurgetime;
465 if (nextpurgetime > realtime)
467 nextpurgetime = realtime + 0.05;
468 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
470 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
472 R_FreeTexture(pic->tex);
479 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
484 crc = CRC_Block((unsigned char *)picname, strlen(picname));
485 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
486 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
487 if (!strcmp (picname, pic->name))
492 if (pic->tex && pic->width == width && pic->height == height)
494 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
502 if (numcachepics == MAX_CACHED_PICS)
504 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
505 // FIXME: support NULL in callers?
506 return cachepics; // return the first one
508 pic = cachepics + (numcachepics++);
509 strlcpy (pic->name, picname, sizeof(pic->name));
511 pic->chain = cachepichash[hashkey];
512 cachepichash[hashkey] = pic;
517 pic->height = height;
519 R_FreeTexture(pic->tex);
520 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0) | TEXF_ALLOWUPDATES, -1, NULL);
524 void Draw_FreePic(const char *picname)
529 // this doesn't really free the pic, but does free it's texture
530 crc = CRC_Block((unsigned char *)picname, strlen(picname));
531 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
532 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
534 if (!strcmp (picname, pic->name) && pic->tex)
536 R_FreeTexture(pic->tex);
545 static float snap_to_pixel_x(float x, float roundUpAt);
546 extern int con_linewidth; // to force rewrapping
547 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
551 char widthfile[MAX_QPATH];
553 fs_offset_t widthbufsize;
555 if(override || !fnt->texpath[0])
557 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
558 // load the cvars when the font is FIRST loader
559 fnt->settings.scale = scale;
560 fnt->settings.voffset = voffset;
561 fnt->settings.antialias = r_font_antialias.integer;
562 fnt->settings.hinting = r_font_hinting.integer;
563 fnt->settings.outline = r_font_postprocess_outline.value;
564 fnt->settings.blur = r_font_postprocess_blur.value;
565 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
566 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
567 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
570 if (fnt->settings.scale <= 0)
571 fnt->settings.scale = 1;
573 if(drawtexturepool == NULL)
574 return; // before gl_draw_start, so will be loaded later
578 // clear freetype font
579 Font_UnloadFont(fnt->ft2);
584 if(fnt->req_face != -1)
586 if(!Font_LoadFont(fnt->texpath, fnt))
587 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
590 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
591 if(fnt->tex == r_texture_notexture)
593 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
595 if (!fnt->fallbacks[i][0])
597 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
598 if(fnt->tex != r_texture_notexture)
601 if(fnt->tex == r_texture_notexture)
603 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
604 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
607 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
610 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
612 // unspecified width == 1 (base width)
613 for(ch = 0; ch < 256; ++ch)
614 fnt->width_of[ch] = 1;
616 // FIXME load "name.width", if it fails, fill all with 1
617 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
619 float extraspacing = 0;
620 const char *p = widthbuf;
625 if(!COM_ParseToken_Simple(&p, false, false))
643 fnt->width_of[ch] = atof(com_token) + extraspacing;
647 if(!strcmp(com_token, "extraspacing"))
649 if(!COM_ParseToken_Simple(&p, false, false))
651 extraspacing = atof(com_token);
653 else if(!strcmp(com_token, "scale"))
655 if(!COM_ParseToken_Simple(&p, false, false))
657 fnt->settings.scale = atof(com_token);
661 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
662 if(!COM_ParseToken_Simple(&p, false, false))
674 for (i = 0; i < MAX_FONT_SIZES; ++i)
676 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
679 for(ch = 0; ch < 256; ++ch)
680 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
684 maxwidth = fnt->width_of[0];
685 for(i = 1; i < 256; ++i)
686 maxwidth = max(maxwidth, fnt->width_of[i]);
687 fnt->maxwidth = maxwidth;
689 // fix up maxwidth for overlap
690 fnt->maxwidth *= fnt->settings.scale;
692 if(fnt == FONT_CONSOLE)
693 con_linewidth = -1; // rewrap console in next frame
696 extern cvar_t developer_font;
697 dp_font_t *FindFont(const char *title, qboolean allocate_new)
702 for(i = 0; i < dp_fonts.maxsize; ++i)
703 if(!strcmp(dp_fonts.f[i].title, title))
704 return &dp_fonts.f[i];
705 // if not found - try allocate
708 // find any font with empty title
709 for(i = 0; i < dp_fonts.maxsize; ++i)
711 if(!strcmp(dp_fonts.f[i].title, ""))
713 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
714 return &dp_fonts.f[i];
717 // if no any 'free' fonts - expand buffer
718 i = dp_fonts.maxsize;
719 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
720 if (developer_font.integer)
721 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", i, dp_fonts.maxsize);
722 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
723 // register a font in first expanded slot
724 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
725 return &dp_fonts.f[i];
730 static float snap_to_pixel_x(float x, float roundUpAt)
732 float pixelpos = x * vid.width / vid_conwidth.value;
733 int snap = (int) pixelpos;
734 if (pixelpos - snap >= roundUpAt) ++snap;
735 return ((float)snap * vid_conwidth.value / vid.width);
737 x = (int)(x * vid.width / vid_conwidth.value);
738 x = (x * vid_conwidth.value / vid.width);
743 static float snap_to_pixel_y(float y, float roundUpAt)
745 float pixelpos = y * vid.height / vid_conheight.value;
746 int snap = (int) pixelpos;
747 if (pixelpos - snap > roundUpAt) ++snap;
748 return ((float)snap * vid_conheight.value / vid.height);
750 y = (int)(y * vid.height / vid_conheight.value);
751 y = (y * vid_conheight.value / vid.height);
756 static void LoadFont_f(void)
760 const char *filelist, *c, *cm;
761 float sz, scale, voffset;
762 char mainfont[MAX_QPATH];
766 Con_Printf("Available font commands:\n");
767 for(i = 0; i < dp_fonts.maxsize; ++i)
768 if (dp_fonts.f[i].title[0])
769 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
770 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
771 "can specify multiple fonts and faces\n"
772 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
773 "to load face 2 of the font gfx/vera-sans and use face 1\n"
774 "of gfx/fallback as fallback font.\n"
775 "You can also specify a list of font sizes to load, like this:\n"
776 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
777 "In many cases, 8 12 16 24 32 should be a good choice.\n"
779 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
780 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
784 f = FindFont(Cmd_Argv(1), true);
787 Con_Printf("font function not found\n");
792 filelist = "gfx/conchars";
794 filelist = Cmd_Argv(2);
796 memset(f->fallbacks, 0, sizeof(f->fallbacks));
797 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
799 // first font is handled "normally"
800 c = strchr(filelist, ':');
801 cm = strchr(filelist, ',');
802 if(c && (!cm || c < cm))
803 f->req_face = atoi(c+1);
810 if(!c || (c - filelist) > MAX_QPATH)
811 strlcpy(mainfont, filelist, sizeof(mainfont));
814 memcpy(mainfont, filelist, c - filelist);
815 mainfont[c - filelist] = 0;
818 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
820 c = strchr(filelist, ',');
826 c = strchr(filelist, ':');
827 cm = strchr(filelist, ',');
828 if(c && (!cm || c < cm))
829 f->fallback_faces[i] = atoi(c+1);
832 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
835 if(!c || (c-filelist) > MAX_QPATH)
837 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
841 memcpy(f->fallbacks[i], filelist, c - filelist);
842 f->fallbacks[i][c - filelist] = 0;
846 // for now: by default load only one size: the default size
848 for(i = 1; i < MAX_FONT_SIZES; ++i)
849 f->req_sizes[i] = -1;
855 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
858 if (!strcmp(Cmd_Argv(i), "scale"))
862 scale = atof(Cmd_Argv(i));
865 if (!strcmp(Cmd_Argv(i), "voffset"))
869 voffset = atof(Cmd_Argv(i));
872 // parse one of sizes
873 sz = atof(Cmd_Argv(i));
874 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
876 f->req_sizes[sizes] = sz;
882 LoadFont(true, mainfont, f, scale, voffset);
890 static void gl_draw_start(void)
893 drawtexturepool = R_AllocTexturePool();
896 memset(cachepichash, 0, sizeof(cachepichash));
900 // load default font textures
901 for(i = 0; i < dp_fonts.maxsize; ++i)
902 if (dp_fonts.f[i].title[0])
903 LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
905 // draw the loading screen so people have something to see in the newly opened window
906 SCR_UpdateLoadingScreen(true);
909 static void gl_draw_shutdown(void)
913 R_FreeTexturePool(&drawtexturepool);
916 memset(cachepichash, 0, sizeof(cachepichash));
919 static void gl_draw_newmap(void)
924 void GL_Draw_Init (void)
928 Cvar_RegisterVariable(&r_font_postprocess_blur);
929 Cvar_RegisterVariable(&r_font_postprocess_outline);
930 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
931 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
932 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
933 Cvar_RegisterVariable(&r_font_hinting);
934 Cvar_RegisterVariable(&r_font_antialias);
935 Cvar_RegisterVariable(&r_textshadow);
936 Cvar_RegisterVariable(&r_textbrightness);
937 Cvar_RegisterVariable(&r_textcontrast);
939 // allocate fonts storage
940 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
941 dp_fonts.maxsize = MAX_FONTS;
942 dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
943 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
945 // assign starting font names
946 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
947 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
948 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
949 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
950 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
951 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
952 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
953 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
954 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
955 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
956 if(!FONT_USER(i)->title[0])
957 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
959 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
960 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
963 void _DrawQ_Setup(void)
965 r_viewport_t viewport;
966 if (r_refdef.draw2dstage)
968 r_refdef.draw2dstage = true;
970 R_Viewport_InitOrtho(&viewport, &identitymatrix, r_refdef.view.x, vid.height - r_refdef.view.y - r_refdef.view.height, r_refdef.view.width, r_refdef.view.height, 0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100, NULL);
971 R_SetViewport(&viewport);
972 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
973 GL_DepthFunc(GL_LEQUAL);
974 GL_PolygonOffset(0,0);
975 GL_CullFace(GL_NONE);
976 R_EntityMatrix(&identitymatrix);
980 GL_PolygonOffset(0, 0);
984 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
987 qboolean r_draw2d_force = false;
988 static void _DrawQ_ProcessDrawFlag(int flags)
992 if(!r_draw2d.integer && !r_draw2d_force)
994 if(flags == DRAWFLAG_ADDITIVE)
995 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
996 else if(flags == DRAWFLAG_MODULATE)
997 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
998 else if(flags == DRAWFLAG_2XMODULATE)
999 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
1000 else if(flags == DRAWFLAG_SCREEN)
1001 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
1003 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1006 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1010 _DrawQ_ProcessDrawFlag(flags);
1011 if(!r_draw2d.integer && !r_draw2d_force)
1014 R_Mesh_ResetTextureState();
1015 floats[12] = 0.0f;floats[13] = 0.0f;
1016 floats[14] = 1.0f;floats[15] = 0.0f;
1017 floats[16] = 1.0f;floats[17] = 1.0f;
1018 floats[18] = 0.0f;floats[19] = 1.0f;
1019 floats[20] = floats[24] = floats[28] = floats[32] = red;
1020 floats[21] = floats[25] = floats[29] = floats[33] = green;
1021 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1022 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1028 height = pic->height;
1029 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1032 // AK07: lets be texel correct on the corners
1034 float horz_offset = 0.5f / pic->width;
1035 float vert_offset = 0.5f / pic->height;
1037 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1038 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1039 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1040 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1045 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1047 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1048 floats[0] = floats[9] = x;
1049 floats[1] = floats[4] = y;
1050 floats[3] = floats[6] = x + width;
1051 floats[7] = floats[10] = y + height;
1053 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1054 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1057 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)
1060 float af = DEG2RAD(-angle); // forward
1061 float ar = DEG2RAD(-angle + 90); // right
1062 float sinaf = sin(af);
1063 float cosaf = cos(af);
1064 float sinar = sin(ar);
1065 float cosar = cos(ar);
1067 _DrawQ_ProcessDrawFlag(flags);
1068 if(!r_draw2d.integer && !r_draw2d_force)
1071 R_Mesh_ResetTextureState();
1077 height = pic->height;
1078 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1081 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1083 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1086 floats[0] = x - cosaf*org_x - cosar*org_y;
1087 floats[1] = y - sinaf*org_x - sinar*org_y;
1090 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1091 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1094 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1095 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1098 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1099 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1101 floats[12] = 0.0f;floats[13] = 0.0f;
1102 floats[14] = 1.0f;floats[15] = 0.0f;
1103 floats[16] = 1.0f;floats[17] = 1.0f;
1104 floats[18] = 0.0f;floats[19] = 1.0f;
1105 floats[20] = floats[24] = floats[28] = floats[32] = red;
1106 floats[21] = floats[25] = floats[29] = floats[33] = green;
1107 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1108 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1110 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1111 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1114 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1118 _DrawQ_ProcessDrawFlag(flags);
1119 if(!r_draw2d.integer && !r_draw2d_force)
1122 R_Mesh_ResetTextureState();
1123 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1125 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1126 floats[0] = floats[9] = x;
1127 floats[1] = floats[4] = y;
1128 floats[3] = floats[6] = x + width;
1129 floats[7] = floats[10] = y + height;
1130 floats[12] = 0.0f;floats[13] = 0.0f;
1131 floats[14] = 1.0f;floats[15] = 0.0f;
1132 floats[16] = 1.0f;floats[17] = 1.0f;
1133 floats[18] = 0.0f;floats[19] = 1.0f;
1134 floats[20] = floats[24] = floats[28] = floats[32] = red;
1135 floats[21] = floats[25] = floats[29] = floats[33] = green;
1136 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1137 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1139 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1140 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1143 /// color tag printing
1144 static const vec4_t string_colors[] =
1147 // LordHavoc: why on earth is cyan before magenta in Quake3?
1148 // LordHavoc: note: Doom3 uses white for [0] and [7]
1149 {0.0, 0.0, 0.0, 1.0}, // black
1150 {1.0, 0.0, 0.0, 1.0}, // red
1151 {0.0, 1.0, 0.0, 1.0}, // green
1152 {1.0, 1.0, 0.0, 1.0}, // yellow
1153 {0.0, 0.0, 1.0, 1.0}, // blue
1154 {0.0, 1.0, 1.0, 1.0}, // cyan
1155 {1.0, 0.0, 1.0, 1.0}, // magenta
1156 {1.0, 1.0, 1.0, 1.0}, // white
1157 // [515]'s BX_COLOREDTEXT extension
1158 {1.0, 1.0, 1.0, 0.5}, // half transparent
1159 {0.5, 0.5, 0.5, 1.0} // half brightness
1160 // Black's color table
1161 //{1.0, 1.0, 1.0, 1.0},
1162 //{1.0, 0.0, 0.0, 1.0},
1163 //{0.0, 1.0, 0.0, 1.0},
1164 //{0.0, 0.0, 1.0, 1.0},
1165 //{1.0, 1.0, 0.0, 1.0},
1166 //{0.0, 1.0, 1.0, 1.0},
1167 //{1.0, 0.0, 1.0, 1.0},
1168 //{0.1, 0.1, 0.1, 1.0}
1171 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1173 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1175 float C = r_textcontrast.value;
1176 float B = r_textbrightness.value;
1177 if (colorindex & 0x10000) // that bit means RGB color
1179 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1180 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1181 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1182 color[3] = (colorindex & 0xf) / 15.0;
1185 Vector4Copy(string_colors[colorindex], color);
1186 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1189 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1190 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1194 // NOTE: this function always draws exactly one character if maxwidth <= 0
1195 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)
1197 const char *text_start = text;
1198 int colorindex = STRING_COLOR_DEFAULT;
1201 Uchar ch, mapch, nextch;
1202 Uchar prevch = 0; // used for kerning
1207 ft2_font_map_t *fontmap = NULL;
1208 ft2_font_map_t *map = NULL;
1209 //ft2_font_map_t *prevmap = NULL;
1210 ft2_font_t *ft2 = fnt->ft2;
1212 qboolean snap = true;
1213 qboolean least_one = false;
1214 float dw; // display w
1215 //float dh; // display h
1216 const float *width_of;
1223 // do this in the end
1224 w *= fnt->settings.scale;
1225 h *= fnt->settings.scale;
1227 // find the most fitting size:
1231 map_index = Font_IndexForSize(ft2, h, &w, &h);
1233 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1234 fontmap = Font_MapForIndex(ft2, map_index);
1243 if (!outcolor || *outcolor == -1)
1244 colorindex = STRING_COLOR_DEFAULT;
1246 colorindex = *outcolor;
1248 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1249 // ftbase_x = snap_to_pixel_x(0);
1254 maxwidth = -maxwidth;
1258 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1261 width_of = fontmap->width_of;
1263 width_of = fnt->width_of;
1265 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1268 nextch = ch = u8_getnchar(text, &text, bytes_left);
1269 i = text - text_start;
1272 if (ch == ' ' && !fontmap)
1274 if(!least_one || i0) // never skip the first character
1275 if(x + width_of[(int) ' '] * dw > maxwidth)
1278 break; // oops, can't draw this
1280 x += width_of[(int) ' '] * dw;
1283 // i points to the char after ^
1284 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1286 ch = *text; // colors are ascii, so no u8_ needed
1287 if (ch <= '9' && ch >= '0') // ^[0-9] found
1289 colorindex = ch - '0';
1294 // i points to the char after ^...
1295 // i+3 points to 3 in ^x123
1296 // i+3 == *maxlen would mean that char is missing
1297 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1299 // building colorindex...
1300 ch = tolower(text[1]);
1301 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1302 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1303 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1304 else tempcolorindex = 0;
1307 ch = tolower(text[2]);
1308 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1309 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1310 else tempcolorindex = 0;
1313 ch = tolower(text[3]);
1314 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1315 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1316 else tempcolorindex = 0;
1319 colorindex = tempcolorindex | 0xf;
1320 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1328 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1337 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1344 map = ft2_oldstyle_map;
1346 if(!least_one || i0) // never skip the first character
1347 if(x + width_of[ch] * dw > maxwidth)
1350 break; // oops, can't draw this
1352 x += width_of[ch] * dw;
1354 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1356 map = FontMap_FindForChar(fontmap, ch);
1359 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1365 mapch = ch - map->start;
1366 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1368 x += map->glyphs[mapch].advance_x * dw;
1377 *outcolor = colorindex;
1382 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)
1384 int shadow, colorindex = STRING_COLOR_DEFAULT;
1386 float x = startx, y, s, t, u, v, thisw;
1387 float *av, *at, *ac;
1390 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1391 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1392 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1393 Uchar ch, mapch, nextch;
1394 Uchar prevch = 0; // used for kerning
1397 //ft2_font_map_t *prevmap = NULL; // the previous map
1398 ft2_font_map_t *map = NULL; // the currently used map
1399 ft2_font_map_t *fontmap = NULL; // the font map for the size
1401 const char *text_start = text;
1403 ft2_font_t *ft2 = fnt->ft2;
1404 qboolean snap = true;
1408 const float *width_of;
1411 tw = R_TextureWidth(fnt->tex);
1412 th = R_TextureHeight(fnt->tex);
1420 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1421 w *= fnt->settings.scale;
1422 h *= fnt->settings.scale;
1427 map_index = Font_IndexForSize(ft2, h, &w, &h);
1429 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1430 fontmap = Font_MapForIndex(ft2, map_index);
1436 // draw the font at its baseline when using freetype
1438 ftbase_y = dh * (4.5/6.0);
1443 _DrawQ_ProcessDrawFlag(flags);
1444 if(!r_draw2d.integer && !r_draw2d_force)
1445 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1447 R_Mesh_ResetTextureState();
1449 R_Mesh_TexBind(0, fnt->tex);
1450 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1457 //ftbase_x = snap_to_pixel_x(ftbase_x);
1460 startx = snap_to_pixel_x(startx, 0.4);
1461 starty = snap_to_pixel_y(starty, 0.4);
1462 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1465 pix_x = vid.width / vid_conwidth.value;
1466 pix_y = vid.height / vid_conheight.value;
1469 width_of = fontmap->width_of;
1471 width_of = fnt->width_of;
1473 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1478 if (!outcolor || *outcolor == -1)
1479 colorindex = STRING_COLOR_DEFAULT;
1481 colorindex = *outcolor;
1483 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1490 x += r_textshadow.value * vid.width / vid_conwidth.value;
1491 y += r_textshadow.value * vid.height / vid_conheight.value;
1494 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1496 nextch = ch = u8_getnchar(text, &text, bytes_left);
1497 i = text - text_start;
1500 if (ch == ' ' && !fontmap)
1502 x += width_of[(int) ' '] * dw;
1505 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1507 ch = *text; // colors are ascii, so no u8_ needed
1508 if (ch <= '9' && ch >= '0') // ^[0-9] found
1510 colorindex = ch - '0';
1511 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1516 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1518 // building colorindex...
1519 ch = tolower(text[1]);
1520 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1521 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1522 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1523 else tempcolorindex = 0;
1526 ch = tolower(text[2]);
1527 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1528 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1529 else tempcolorindex = 0;
1532 ch = tolower(text[3]);
1533 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1534 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1535 else tempcolorindex = 0;
1538 colorindex = tempcolorindex | 0xf;
1539 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1540 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1541 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1549 else if (ch == STRING_COLOR_TAG)
1558 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1559 // this way we don't need to rebind fnt->tex for every old-style character
1560 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1563 x += 1.0/pix_x * r_textshadow.value;
1564 y += 1.0/pix_y * r_textshadow.value;
1566 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1574 if (map != ft2_oldstyle_map)
1578 // switching from freetype to non-freetype rendering
1579 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1580 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1586 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1587 map = ft2_oldstyle_map;
1591 //num = (unsigned char) text[i];
1592 //thisw = fnt->width_of[num];
1593 thisw = fnt->width_of[ch];
1594 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1595 s = (ch & 15)*0.0625f + (0.5f / tw);
1596 t = (ch >> 4)*0.0625f + (0.5f / th);
1597 u = 0.0625f * thisw - (1.0f / tw);
1598 v = 0.0625f - (1.0f / th);
1599 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1600 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1601 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1602 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1603 at[ 0] = s ; at[ 1] = t ;
1604 at[ 2] = s+u ; at[ 3] = t ;
1605 at[ 4] = s+u ; at[ 5] = t+v ;
1606 at[ 6] = s ; at[ 7] = t+v ;
1607 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1608 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1609 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1610 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1615 if (batchcount >= QUADELEMENTS_MAXQUADS)
1617 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1618 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1624 x += width_of[ch] * dw;
1626 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1628 // new charmap - need to render
1631 // we need a different character map, render what we currently have:
1632 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1633 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1640 map = FontMap_FindForChar(fontmap, ch);
1643 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1650 // this shouldn't happen
1655 R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1658 mapch = ch - map->start;
1659 thisw = map->glyphs[mapch].advance_x;
1663 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1670 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1671 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1672 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1673 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1674 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1675 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1676 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1677 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1678 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1679 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1680 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1681 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1690 if (batchcount >= QUADELEMENTS_MAXQUADS)
1692 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1693 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1705 x -= 1.0/pix_x * r_textshadow.value;
1706 y -= 1.0/pix_y * r_textshadow.value;
1712 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1713 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1717 *outcolor = colorindex;
1719 // note: this relies on the proper text (not shadow) being drawn last
1723 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)
1725 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1728 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)
1730 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1733 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1735 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1738 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1740 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1745 // no ^xrgb management
1746 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1748 int color, numchars = 0;
1749 char *outputend2c = output2c + maxoutchars - 2;
1750 if (!outcolor || *outcolor == -1)
1751 color = STRING_COLOR_DEFAULT;
1755 maxreadchars = 1<<30;
1756 textend = text + maxreadchars;
1757 while (text != textend && *text)
1759 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1761 if (text[1] == STRING_COLOR_TAG)
1763 else if (text[1] >= '0' && text[1] <= '9')
1765 color = text[1] - '0';
1770 if (output2c >= outputend2c)
1772 *output2c++ = *text++;
1773 *output2c++ = color;
1776 output2c[0] = output2c[1] = 0;
1783 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)
1787 _DrawQ_ProcessDrawFlag(flags);
1788 if(!r_draw2d.integer && !r_draw2d_force)
1791 R_Mesh_ResetTextureState();
1797 height = pic->height;
1798 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1801 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1803 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1804 floats[0] = floats[9] = x;
1805 floats[1] = floats[4] = y;
1806 floats[3] = floats[6] = x + width;
1807 floats[7] = floats[10] = y + height;
1808 floats[12] = s1;floats[13] = t1;
1809 floats[14] = s2;floats[15] = t2;
1810 floats[16] = s4;floats[17] = t4;
1811 floats[18] = s3;floats[19] = t3;
1812 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1813 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1814 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1815 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1817 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1818 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1821 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1823 _DrawQ_ProcessDrawFlag(flags);
1824 if(!r_draw2d.integer && !r_draw2d_force)
1827 R_Mesh_ResetTextureState();
1828 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1830 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1831 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1834 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1838 _DrawQ_ProcessDrawFlag(flags);
1839 if(!r_draw2d.integer && !r_draw2d_force)
1843 switch(vid.renderpath)
1845 case RENDERPATH_GL11:
1846 case RENDERPATH_GL13:
1847 case RENDERPATH_GL20:
1848 case RENDERPATH_CGGL:
1850 qglBegin(GL_LINE_LOOP);
1851 for (num = 0;num < mesh->num_vertices;num++)
1853 if (mesh->data_color4f)
1854 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]);
1855 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1860 case RENDERPATH_D3D9:
1861 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1863 case RENDERPATH_D3D10:
1864 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1866 case RENDERPATH_D3D11:
1867 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1872 //[515]: this is old, delete
1873 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1875 _DrawQ_ProcessDrawFlag(flags);
1876 if(!r_draw2d.integer && !r_draw2d_force)
1879 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1881 switch(vid.renderpath)
1883 case RENDERPATH_GL11:
1884 case RENDERPATH_GL13:
1885 case RENDERPATH_GL20:
1886 case RENDERPATH_CGGL:
1889 //qglLineWidth(width);CHECKGLERROR
1891 GL_Color(r,g,b,alpha);
1894 qglVertex2f(x1, y1);
1895 qglVertex2f(x2, y2);
1899 case RENDERPATH_D3D9:
1900 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1902 case RENDERPATH_D3D10:
1903 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1905 case RENDERPATH_D3D11:
1906 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1911 void DrawQ_SetClipArea(float x, float y, float width, float height)
1916 // We have to convert the con coords into real coords
1917 // OGL uses top to bottom
1918 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1919 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1920 iw = (int)(width * ((float)vid.width / vid_conwidth.integer));
1921 ih = (int)(height * ((float)vid.height / vid_conheight.integer));
1922 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1924 GL_ScissorTest(true);
1927 void DrawQ_ResetClipArea(void)
1930 GL_ScissorTest(false);
1933 void DrawQ_Finish(void)
1935 r_refdef.draw2dstage = false;
1938 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1939 void R_DrawGamma(void)
1942 switch(vid.renderpath)
1944 case RENDERPATH_GL20:
1945 case RENDERPATH_CGGL:
1946 case RENDERPATH_D3D9:
1947 case RENDERPATH_D3D10:
1948 case RENDERPATH_D3D11:
1949 if (vid_usinghwgamma || v_glslgamma.integer)
1952 case RENDERPATH_GL13:
1953 case RENDERPATH_GL11:
1954 if (vid_usinghwgamma)
1958 // all the blends ignore depth
1959 R_Mesh_ResetTextureState();
1960 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1962 GL_DepthRange(0, 1);
1963 GL_PolygonOffset(0, 0);
1964 GL_DepthTest(false);
1965 if (v_color_enable.integer)
1967 c[0] = v_color_white_r.value;
1968 c[1] = v_color_white_g.value;
1969 c[2] = v_color_white_b.value;
1972 c[0] = c[1] = c[2] = v_contrast.value;
1973 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1975 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1976 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1978 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
1979 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
1980 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1981 VectorScale(c, 0.5, c);
1984 if (v_color_enable.integer)
1986 c[0] = v_color_black_r.value;
1987 c[1] = v_color_black_g.value;
1988 c[2] = v_color_black_b.value;
1991 c[0] = c[1] = c[2] = v_brightness.value;
1992 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1994 GL_BlendFunc(GL_ONE, GL_ONE);
1995 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
1996 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
1997 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);