X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=gl_draw.c;h=4e0bfd6fb43072d3d23bbda25b8045b9f5fa0027;hp=7f33797de48a4490897a4bc8d607332db0c6c493;hb=0766a43538f3b526cba347157d5259bb3e5785a9;hpb=2d4ac18bc2061b2330fe886691af75bf86ca21db diff --git a/gl_draw.c b/gl_draw.c index 7f33797d..4e0bfd6f 100644 --- a/gl_draw.c +++ b/gl_draw.c @@ -23,276 +23,57 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "wad.h" #include "cl_video.h" -#include "cl_dyntexture.h" #include "ft2.h" #include "ft2_fontdefs.h" -dp_font_t dp_fonts[MAX_FONTS] = {{0}}; +struct cachepic_s +{ + // size of pic + int width, height; + // this flag indicates that it should be loaded and unloaded on demand + int autoload; + // texture flags to upload with + int texflags; + // texture may be freed after a while + int lastusedframe; + // renderable texture + skinframe_t *skinframe; + // used for hash lookups + struct cachepic_s *chain; + // flags - CACHEPICFLAG_NEWPIC for example + unsigned int flags; + // name of pic + char name[MAX_QPATH]; +}; + +dp_fonts_t dp_fonts; +static mempool_t *fonts_mempool = NULL; 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)"}; 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)"}; 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)"}; -extern cvar_t v_glslgamma; +cvar_t r_font_postprocess_blur = {CVAR_SAVE, "r_font_postprocess_blur", "0", "font blur amount"}; +cvar_t r_font_postprocess_outline = {CVAR_SAVE, "r_font_postprocess_outline", "0", "font outline amount"}; +cvar_t r_font_postprocess_shadow_x = {CVAR_SAVE, "r_font_postprocess_shadow_x", "0", "font shadow X shift amount, applied during outlining"}; +cvar_t r_font_postprocess_shadow_y = {CVAR_SAVE, "r_font_postprocess_shadow_y", "0", "font shadow Y shift amount, applied during outlining"}; +cvar_t r_font_postprocess_shadow_z = {CVAR_SAVE, "r_font_postprocess_shadow_z", "0", "font shadow Z shift amount, applied during blurring"}; +cvar_t r_font_hinting = {CVAR_SAVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"}; +cvar_t r_font_antialias = {CVAR_SAVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */}; +cvar_t r_nearest_2d = {CVAR_SAVE, "r_nearest_2d", "0", "use nearest filtering on all 2d textures (including conchars)"}; +cvar_t r_nearest_conchars = {CVAR_SAVE, "r_nearest_conchars", "0", "use nearest filtering on conchars texture"}; //============================================================================= /* Support Routines */ -#define FONT_FILESIZE 13468 static cachepic_t *cachepichash[CACHEPICHASHSIZE]; static cachepic_t cachepics[MAX_CACHED_PICS]; static int numcachepics; -static rtexturepool_t *drawtexturepool; - -static const unsigned char concharimage[FONT_FILESIZE] = -{ -#include "lhfont.h" -}; - -static rtexture_t *draw_generateconchars(void) -{ - int i; - unsigned char *data; - double random; - rtexture_t *tex; - - data = LoadTGA_BGRA (concharimage, FONT_FILESIZE); -// Gold numbers - for (i = 0;i < 8192;i++) - { - random = lhrandom (0.0,1.0); - data[i*4+3] = data[i*4+0]; - data[i*4+2] = 83 + (unsigned char)(random * 64); - data[i*4+1] = 71 + (unsigned char)(random * 32); - data[i*4+0] = 23 + (unsigned char)(random * 16); - } -// White chars - for (i = 8192;i < 32768;i++) - { - random = lhrandom (0.0,1.0); - data[i*4+3] = data[i*4+0]; - data[i*4+2] = 95 + (unsigned char)(random * 64); - data[i*4+1] = 95 + (unsigned char)(random * 64); - data[i*4+0] = 95 + (unsigned char)(random * 64); - } -// Gold numbers - for (i = 32768;i < 40960;i++) - { - random = lhrandom (0.0,1.0); - data[i*4+3] = data[i*4+0]; - data[i*4+2] = 83 + (unsigned char)(random * 64); - data[i*4+1] = 71 + (unsigned char)(random * 32); - data[i*4+0] = 23 + (unsigned char)(random * 16); - } -// Red chars - for (i = 40960;i < 65536;i++) - { - random = lhrandom (0.0,1.0); - data[i*4+3] = data[i*4+0]; - data[i*4+2] = 96 + (unsigned char)(random * 64); - data[i*4+1] = 43 + (unsigned char)(random * 32); - data[i*4+0] = 27 + (unsigned char)(random * 32); - } - -#if 0 - Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, data); -#endif - - tex = R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, data, TEXTYPE_BGRA, TEXF_ALPHA, NULL); - Mem_Free(data); - return tex; -} - -static rtexture_t *draw_generateditherpattern(void) -{ - int x, y; - unsigned char pixels[8][8]; - for (y = 0;y < 8;y++) - for (x = 0;x < 8;x++) - pixels[y][x] = ((x^y) & 4) ? 254 : 0; - return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST, palette_bgra_transparent); -} - -typedef struct embeddedpic_s -{ - const char *name; - int width; - int height; - const char *pixels; -} -embeddedpic_t; - -static const embeddedpic_t embeddedpics[] = -{ - { - "gfx/prydoncursor001", 16, 16, - "477777774......." - "77.....6........" - "7.....6........." - "7....6.........." - "7.....6........." - "7..6...6........" - "7.6.6...6......." - "76...6...6......" - "4.....6.6......." - ".......6........" - "................" - "................" - "................" - "................" - "................" - "................" - }, - { - "ui/mousepointer", 16, 16, - "477777774......." - "77.....6........" - "7.....6........." - "7....6.........." - "7.....6........." - "7..6...6........" - "7.6.6...6......." - "76...6...6......" - "4.....6.6......." - ".......6........" - "................" - "................" - "................" - "................" - "................" - "................" - }, - { - "gfx/crosshair1", 16, 16, - "................" - "................" - "................" - "...33......33..." - "...355....553..." - "....577..775...." - ".....77..77....." - "................" - "................" - ".....77..77....." - "....577..775...." - "...355....553..." - "...33......33..." - "................" - "................" - "................" - }, - { - "gfx/crosshair2", 16, 16, - "................" - "................" - "................" - "...3........3..." - "....5......5...." - ".....7....7....." - "......7..7......" - "................" - "................" - "......7..7......" - ".....7....7....." - "....5......5...." - "...3........3..." - "................" - "................" - "................" - }, - { - "gfx/crosshair3", 16, 16, - "................" - ".......77......." - ".......77......." - "................" - "................" - ".......44......." - ".......44......." - ".77..44..44..77." - ".77..44..44..77." - ".......44......." - ".......44......." - "................" - "................" - ".......77......." - ".......77......." - "................" - }, - { - "gfx/crosshair4", 16, 16, - "................" - "................" - "................" - "................" - "................" - "................" - "................" - "................" - "........7777777." - "........752....." - "........72......" - "........7......." - "........7......." - "........7......." - "........7......." - "................" - }, - { - "gfx/crosshair5", 8, 8, - "........" - "........" - "....7..." - "........" - "..7.7.7." - "........" - "....7..." - "........" - }, - { - "gfx/crosshair6", 2, 2, - "77" - "77" - }, - { - "gfx/crosshair7", 16, 16, - "................" - ".3............3." - "..5...2332...5.." - "...7.3....3.7..." - "....7......7...." - "...3.7....7.3..." - "..2...7..7...2.." - "..3..........3.." - "..3..........3.." - "..2...7..7...2.." - "...3.7....7.3..." - "....7......7...." - "...7.3....3.7..." - "..5...2332...5.." - ".3............3." - "................" - }, - {NULL, 0, 0, NULL} -}; - -static rtexture_t *draw_generatepic(const char *name, qboolean quiet) -{ - const embeddedpic_t *p; - for (p = embeddedpics;p->name;p++) - if (!strcmp(name, p->name)) - return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA, palette_bgra_embeddedpic); - if (!strcmp(name, "gfx/conchars")) - return draw_generateconchars(); - if (!strcmp(name, "gfx/colorcontrol/ditherpattern")) - return draw_generateditherpattern(); - if (!quiet) - Con_Printf("Draw_CachePic: failed to load %s\n", name); - return r_texture_notexture; -} +rtexturepool_t *drawtexturepool; +int draw_frame = 1; /* ================ @@ -303,18 +84,46 @@ Draw_CachePic cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags) { int crc, hashkey; - unsigned char *pixels; cachepic_t *pic; - fs_offset_t lmpsize; - unsigned char *lmpdata; - char lmpname[MAX_QPATH]; + int texflags; + + texflags = TEXF_ALPHA; + if (!(cachepicflags & CACHEPICFLAG_NOCLAMP)) + texflags |= TEXF_CLAMP; + if (cachepicflags & CACHEPICFLAG_MIPMAP) + texflags |= TEXF_MIPMAP; + if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer && gl_texturecompression.integer) + texflags |= TEXF_COMPRESS; + if ((cachepicflags & CACHEPICFLAG_NEAREST) || r_nearest_2d.integer) + texflags |= TEXF_FORCENEAREST; // check whether the picture has already been cached crc = CRC_Block((unsigned char *)path, strlen(path)); hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE; for (pic = cachepichash[hashkey];pic;pic = pic->chain) - if (!strcmp (path, pic->name)) - return pic; + { + if (!strcmp(path, pic->name)) + { + // if it was created (or replaced) by Draw_NewPic, just return it + if (pic->flags & CACHEPICFLAG_NEWPIC) + { + if (pic->skinframe) + R_SkinFrame_MarkUsed(pic->skinframe); + pic->lastusedframe = draw_frame; + return pic; + } + 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 + { + if (!pic->skinframe || !pic->skinframe->base) + goto reload; + if (!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT)) + pic->autoload = false; // caller is making this pic persistent + R_SkinFrame_MarkUsed(pic->skinframe); + pic->lastusedframe = draw_frame; + return pic; + } + } + } if (numcachepics == MAX_CACHED_PICS) { @@ -323,126 +132,81 @@ cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags) return cachepics; // return the first one } pic = cachepics + (numcachepics++); + memset(pic, 0, sizeof(*pic)); strlcpy (pic->name, path, sizeof(pic->name)); // link into list pic->chain = cachepichash[hashkey]; cachepichash[hashkey] = pic; - // check whether it is an dynamic texture (if so, we can directly use its texture handler) - pic->tex = CL_GetDynTexture( path ); - // if so, set the width/height, too - if( pic->tex ) { - pic->width = R_TextureWidth(pic->tex); - pic->height = R_TextureHeight(pic->tex); - // we're done now (early-out) - return pic; - } +reload: + if (pic->skinframe) + R_SkinFrame_PurgeSkinFrame(pic->skinframe); - pic->texflags = TEXF_ALPHA; - if (!(cachepicflags & CACHEPICFLAG_NOCLAMP)) - pic->texflags |= TEXF_CLAMP; - if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer) - pic->texflags |= TEXF_COMPRESS; + pic->flags = cachepicflags; + pic->texflags = texflags; + pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT) != 0; + pic->lastusedframe = draw_frame; - pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT); + // load high quality image (this falls back to low quality too) + pic->skinframe = R_SkinFrame_LoadExternal(pic->name, texflags, (cachepicflags & CACHEPICFLAG_QUIET) == 0, (cachepicflags & CACHEPICFLAG_FAILONMISSING) == 0); - // load a high quality image from disk if possible - pixels = loadimagepixelsbgra(path, false, true); - if (pixels == NULL && !strncmp(path, "gfx/", 4)) - pixels = loadimagepixelsbgra(path+4, false, true); - if (pixels) - { - pic->width = image_width; - pic->height = image_height; - if (!pic->autoload) - pic->tex = R_LoadTexture2D(drawtexturepool, path, image_width, image_height, pixels, TEXTYPE_BGRA, pic->texflags, NULL); - } - else + // get the dimensions of the image we loaded (if it was successful) + if (pic->skinframe && pic->skinframe->base) { - pic->autoload = false; - // never compress the fallback images - pic->texflags &= ~TEXF_COMPRESS; + pic->width = R_TextureWidth(pic->skinframe->base); + pic->height = R_TextureHeight(pic->skinframe->base); } - // now read the low quality version (wad or lmp file), and take the pic - // size from that even if we don't upload the texture, this way the pics - // show up the right size in the menu even if they were replaced with - // higher or lower resolution versions - dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path); - if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize))) - { - if (developer_loading.integer) - Con_Printf("loading lump \"%s\"\n", path); + // check for a low quality version of the pic and use its size if possible, to match the stock hud + Image_GetStockPicSize(pic->name, &pic->width, &pic->height); - if (lmpsize >= 9) - { - pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216; - pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216; - // if no high quality replacement image was found, upload the original low quality texture - if (!pixels) - pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, palette_bgra_transparent); - } - Mem_Free(lmpdata); - } - else if ((lmpdata = W_GetLumpName (path + 4))) - { - if (developer_loading.integer) - Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4); + return pic; +} - if (!strcmp(path, "gfx/conchars")) - { - // conchars is a raw image and with color 0 as transparent instead of 255 - pic->width = 128; - pic->height = 128; - // if no high quality replacement image was found, upload the original low quality texture - if (!pixels) - pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, pic->texflags, palette_bgra_font); - } - else - { - pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216; - pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216; - // if no high quality replacement image was found, upload the original low quality texture - if (!pixels) - pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, palette_bgra_transparent); - } - } +cachepic_t *Draw_CachePic (const char *path) +{ + return Draw_CachePic_Flags (path, 0); // default to persistent! +} - if (pixels) - { - Mem_Free(pixels); - pixels = NULL; - } - else if (pic->tex == NULL) - { - // if it's not found on disk, generate an image - pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0); - pic->width = R_TextureWidth(pic->tex); - pic->height = R_TextureHeight(pic->tex); - } +const char *Draw_GetPicName(cachepic_t *pic) +{ + if (pic == NULL) + return ""; + return pic->name; +} - return pic; +int Draw_GetPicWidth(cachepic_t *pic) +{ + if (pic == NULL) + return 0; + return pic->width; } -cachepic_t *Draw_CachePic (const char *path) +int Draw_GetPicHeight(cachepic_t *pic) { - return Draw_CachePic_Flags (path, 0); + if (pic == NULL) + return 0; + return pic->height; } -int draw_frame = 1; +qboolean Draw_IsPicLoaded(cachepic_t *pic) +{ + if (pic == NULL) + return false; + if (pic->autoload && (!pic->skinframe || !pic->skinframe->base)) + pic->skinframe = R_SkinFrame_LoadExternal(pic->name, pic->texflags, false, true); + // skinframe will only be NULL if the pic was created with CACHEPICFLAG_FAILONMISSING and not found + return pic->skinframe != NULL && pic->skinframe->base != NULL; +} rtexture_t *Draw_GetPicTexture(cachepic_t *pic) { - if (pic->autoload && !pic->tex) - { - pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true); - if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4)) - pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true); - if (pic->tex == NULL) - pic->tex = draw_generatepic(pic->name, true); - } + if (pic == NULL) + return NULL; + if (pic->autoload && (!pic->skinframe || !pic->skinframe->base)) + pic->skinframe = R_SkinFrame_LoadExternal(pic->name, pic->texflags, false, true); pic->lastusedframe = draw_frame; - return pic->tex; + return pic->skinframe ? pic->skinframe->base : NULL; } void Draw_Frame(void) @@ -450,23 +214,16 @@ void Draw_Frame(void) int i; cachepic_t *pic; static double nextpurgetime; - int purgeframe; if (nextpurgetime > realtime) return; nextpurgetime = realtime + 0.05; - purgeframe = draw_frame - 1; for (i = 0, pic = cachepics;i < numcachepics;i++, pic++) - { - if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame) - { - R_FreeTexture(pic->tex); - pic->tex = NULL; - } - } + if (pic->autoload && pic->skinframe && pic->skinframe->base && pic->lastusedframe < draw_frame) + R_SkinFrame_PurgeSkinFrame(pic->skinframe); draw_frame++; } -cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra) +cachepic_t *Draw_NewPic(const char *picname, int width, int height, unsigned char *pixels_bgra, textype_t textype, int texflags) { int crc, hashkey; cachepic_t *pic; @@ -479,35 +236,39 @@ cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, u if (pic) { - if (pic->tex && pic->width == width && pic->height == height) + if (pic->flags & CACHEPICFLAG_NEWPIC && pic->skinframe && pic->skinframe->base && pic->width == width && pic->height == height) { - R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height); + R_UpdateTexture(pic->skinframe->base, pixels_bgra, 0, 0, 0, width, height, 1); + R_SkinFrame_MarkUsed(pic->skinframe); + pic->lastusedframe = draw_frame; return pic; } } else { - if (pic == NULL) + if (numcachepics == MAX_CACHED_PICS) { - if (numcachepics == MAX_CACHED_PICS) - { - Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n"); - // FIXME: support NULL in callers? - return cachepics; // return the first one - } - pic = cachepics + (numcachepics++); - strlcpy (pic->name, picname, sizeof(pic->name)); - // link into list - pic->chain = cachepichash[hashkey]; - cachepichash[hashkey] = pic; + Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n"); + // FIXME: support NULL in callers? + return cachepics; // return the first one } + pic = cachepics + (numcachepics++); + memset(pic, 0, sizeof(*pic)); + strlcpy (pic->name, picname, sizeof(pic->name)); + // link into list + pic->chain = cachepichash[hashkey]; + cachepichash[hashkey] = pic; } + R_SkinFrame_PurgeSkinFrame(pic->skinframe); + + pic->flags = CACHEPICFLAG_NEWPIC; // disable texflags checks in Draw_CachePic + pic->flags |= (texflags & TEXF_CLAMP) ? 0 : CACHEPICFLAG_NOCLAMP; + pic->flags |= (texflags & TEXF_FORCENEAREST) ? CACHEPICFLAG_NEAREST : 0; pic->width = width; pic->height = height; - if (pic->tex) - R_FreeTexture(pic->tex); - pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0) | TEXF_ALLOWUPDATES, NULL); + pic->skinframe = R_SkinFrame_LoadInternalBGRA(picname, texflags, pixels_bgra, width, height, vid.sRGB2D); + pic->lastusedframe = draw_frame; return pic; } @@ -516,15 +277,14 @@ void Draw_FreePic(const char *picname) int crc; int hashkey; cachepic_t *pic; - // this doesn't really free the pic, but does free it's texture + // this doesn't really free the pic, but does free its texture crc = CRC_Block((unsigned char *)picname, strlen(picname)); hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE; for (pic = cachepichash[hashkey];pic;pic = pic->chain) { - if (!strcmp (picname, pic->name) && pic->tex) + if (!strcmp (picname, pic->name) && pic->skinframe) { - R_FreeTexture(pic->tex); - pic->tex = NULL; + R_SkinFrame_PurgeSkinFrame(pic->skinframe); pic->width = 0; pic->height = 0; return; @@ -532,17 +292,33 @@ void Draw_FreePic(const char *picname) } } +static float snap_to_pixel_x(float x, float roundUpAt); extern int con_linewidth; // to force rewrapping -static void LoadFont(qboolean override, const char *name, dp_font_t *fnt) +void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset) { - int i; - float maxwidth, scale; + int i, ch; + float maxwidth; char widthfile[MAX_QPATH]; char *widthbuf; fs_offset_t widthbufsize; if(override || !fnt->texpath[0]) + { strlcpy(fnt->texpath, name, sizeof(fnt->texpath)); + // load the cvars when the font is FIRST loader + fnt->settings.scale = scale; + fnt->settings.voffset = voffset; + fnt->settings.antialias = r_font_antialias.integer; + fnt->settings.hinting = r_font_hinting.integer; + fnt->settings.outline = r_font_postprocess_outline.value; + fnt->settings.blur = r_font_postprocess_blur.value; + fnt->settings.shadowx = r_font_postprocess_shadow_x.value; + fnt->settings.shadowy = r_font_postprocess_shadow_y.value; + fnt->settings.shadowz = r_font_postprocess_shadow_z.value; + } + // fix bad scale + if (fnt->settings.scale <= 0) + fnt->settings.scale = 1; if(drawtexturepool == NULL) return; // before gl_draw_start, so will be loaded later @@ -558,33 +334,45 @@ static void LoadFont(qboolean override, const char *name, dp_font_t *fnt) if(fnt->req_face != -1) { if(!Font_LoadFont(fnt->texpath, fnt)) - Con_Printf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath); + Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath); } - fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex; - if(fnt->tex == r_texture_notexture) + fnt->pic = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION | (r_nearest_conchars.integer ? CACHEPICFLAG_NEAREST : 0) | CACHEPICFLAG_FAILONMISSING); + if(!Draw_IsPicLoaded(fnt->pic)) { - fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex; - strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile)); + for (i = 0; i < MAX_FONT_FALLBACKS; ++i) + { + if (!fnt->fallbacks[i][0]) + break; + fnt->pic = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION | (r_nearest_conchars.integer ? CACHEPICFLAG_NEAREST : 0) | CACHEPICFLAG_FAILONMISSING); + if(Draw_IsPicLoaded(fnt->pic)) + break; + } + if(!Draw_IsPicLoaded(fnt->pic)) + { + fnt->pic = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION | (r_nearest_conchars.integer ? CACHEPICFLAG_NEAREST : 0)); + strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile)); + } + else + dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]); } else dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath); // unspecified width == 1 (base width) - for(i = 1; i < 256; ++i) - fnt->width_of[i] = 1; - scale = 1; + for(ch = 0; ch < 256; ++ch) + fnt->width_of[ch] = 1; // FIXME load "name.width", if it fails, fill all with 1 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize))) { float extraspacing = 0; const char *p = widthbuf; - int ch = 0; + ch = 0; while(ch < 256) { - if(!COM_ParseToken_Simple(&p, false, false)) + if(!COM_ParseToken_Simple(&p, false, false, true)) return; switch(*com_token) @@ -602,25 +390,26 @@ static void LoadFont(qboolean override, const char *name, dp_font_t *fnt) case '+': case '-': case '.': - fnt->width_of[ch++] = atof(com_token) + extraspacing; + fnt->width_of[ch] = atof(com_token) + extraspacing; + ch++; break; default: if(!strcmp(com_token, "extraspacing")) { - if(!COM_ParseToken_Simple(&p, false, false)) + if(!COM_ParseToken_Simple(&p, false, false, true)) return; extraspacing = atof(com_token); } else if(!strcmp(com_token, "scale")) { - if(!COM_ParseToken_Simple(&p, false, false)) + if(!COM_ParseToken_Simple(&p, false, false, true)) return; - scale = atof(com_token); + fnt->settings.scale = atof(com_token); } else { Con_Printf("Warning: skipped unknown font property %s\n", com_token); - if(!COM_ParseToken_Simple(&p, false, false)) + if(!COM_ParseToken_Simple(&p, false, false, true)) return; } break; @@ -630,29 +419,69 @@ static void LoadFont(qboolean override, const char *name, dp_font_t *fnt) Mem_Free(widthbuf); } - maxwidth = fnt->width_of[1]; - for(i = 2; i < 256; ++i) + if(fnt->ft2) + { + for (i = 0; i < MAX_FONT_SIZES; ++i) + { + ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i); + if (!map) + break; + for(ch = 0; ch < 256; ++ch) + map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size); + } + } + + maxwidth = fnt->width_of[0]; + for(i = 1; i < 256; ++i) maxwidth = max(maxwidth, fnt->width_of[i]); fnt->maxwidth = maxwidth; // fix up maxwidth for overlap - fnt->maxwidth *= scale; - fnt->scale = scale; + fnt->maxwidth *= fnt->settings.scale; if(fnt == FONT_CONSOLE) con_linewidth = -1; // rewrap console in next frame } -static dp_font_t *FindFont(const char *title) +extern cvar_t developer_font; +dp_font_t *FindFont(const char *title, qboolean allocate_new) { - int i; - for(i = 0; i < MAX_FONTS; ++i) - if(!strcmp(dp_fonts[i].title, title)) - return &dp_fonts[i]; + int i, oldsize; + + // find font + for(i = 0; i < dp_fonts.maxsize; ++i) + if(!strcmp(dp_fonts.f[i].title, title)) + return &dp_fonts.f[i]; + // if not found - try allocate + if (allocate_new) + { + // find any font with empty title + for(i = 0; i < dp_fonts.maxsize; ++i) + { + if(!strcmp(dp_fonts.f[i].title, "")) + { + strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title)); + return &dp_fonts.f[i]; + } + } + // if no any 'free' fonts - expand buffer + oldsize = dp_fonts.maxsize; + dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND; + if (developer_font.integer) + Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", oldsize, dp_fonts.maxsize); + dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize); + // relink ft2 structures + for(i = 0; i < oldsize; ++i) + if (dp_fonts.f[i].ft2) + dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings; + // register a font in first expanded slot + strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title)); + return &dp_fonts.f[oldsize]; + } return NULL; } -static inline float snap_to_pixel_x(float x, float roundUpAt) +static float snap_to_pixel_x(float x, float roundUpAt) { float pixelpos = x * vid.width / vid_conwidth.value; int snap = (int) pixelpos; @@ -665,7 +494,7 @@ static inline float snap_to_pixel_x(float x, float roundUpAt) */ } -static inline float snap_to_pixel_y(float y, float roundUpAt) +static float snap_to_pixel_y(float y, float roundUpAt) { float pixelpos = y * vid.height / vid_conheight.value; int snap = (int) pixelpos; @@ -681,16 +510,17 @@ static inline float snap_to_pixel_y(float y, float roundUpAt) static void LoadFont_f(void) { dp_font_t *f; - int i, si; - float sz, sn; + int i, sizes; const char *filelist, *c, *cm; + float sz, scale, voffset; char mainfont[MAX_QPATH]; if(Cmd_Argc() < 2) { Con_Printf("Available font commands:\n"); - for(i = 0; i < MAX_FONTS; ++i) - Con_Printf(" loadfont %s gfx/tgafile[...] [sizes...]\n", dp_fonts[i].title); + for(i = 0; i < dp_fonts.maxsize; ++i) + if (dp_fonts.f[i].title[0]) + Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title); Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n" "can specify multiple fonts and faces\n" "Like this: gfx/vera-sans:2,gfx/fallback:1\n" @@ -699,10 +529,13 @@ static void LoadFont_f(void) "You can also specify a list of font sizes to load, like this:\n" "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n" "In many cases, 8 12 16 24 32 should be a good choice.\n" + "custom switches:\n" + " scale x : scale all characters by this amount when rendering (doesnt change line height)\n" + " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n" ); return; } - f = FindFont(Cmd_Argv(1)); + f = FindFont(Cmd_Argv(1), true); if(f == NULL) { Con_Printf("font function not found\n"); @@ -769,32 +602,56 @@ static void LoadFont_f(void) for(i = 1; i < MAX_FONT_SIZES; ++i) f->req_sizes[i] = -1; - // for some reason this argc is 3 even when using 2 arguments here, maybe nexuiz screws up - if(Cmd_Argc() >= 3) + scale = 1; + voffset = 0; + if(Cmd_Argc() >= 4) { - for(i = 0; i < Cmd_Argc()-3; ++i) + for(sizes = 0, i = 3; i < Cmd_Argc(); ++i) { - sz = atof(Cmd_Argv(i+3)); - if (IS_NAN(sz)) // do not use crap sizes + // special switches + if (!strcmp(Cmd_Argv(i), "scale")) + { + i++; + if (i < Cmd_Argc()) + scale = atof(Cmd_Argv(i)); continue; - // now try to scale to our actual size: - if (vid.width > 0) - sn = snap_to_pixel_y(sz, 0.5); - else + } + if (!strcmp(Cmd_Argv(i), "voffset")) { - sn = sz * vid_height.value / vid_conheight.value; - si = (int)sn; - if ( sn - (float)si >= 0.5 ) - ++si; - sn = si * vid_conheight.value / vid_height.value; + i++; + if (i < Cmd_Argc()) + voffset = atof(Cmd_Argv(i)); + continue; + } + + if (sizes == -1) + continue; // no slot for other sizes + + // parse one of sizes + sz = atof(Cmd_Argv(i)); + if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes + { + // search for duplicated sizes + int j; + for (j=0; jreq_sizes[j] == sz) + break; + if (j != sizes) + continue; // sz already in req_sizes, don't add it again + + if (sizes == MAX_FONT_SIZES) + { + Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES); + sizes = -1; + continue; + } + f->req_sizes[sizes] = sz; + sizes++; } - if (!IS_NAN(sn)) - f->req_sizes[i] = sn; - else - f->req_sizes[i] = sz; } } - LoadFont(true, mainfont, f); + + LoadFont(true, mainfont, f, scale, voffset); } /* @@ -805,6 +662,7 @@ Draw_Init static void gl_draw_start(void) { int i; + char vabuf[1024]; drawtexturepool = R_AllocTexturePool(); numcachepics = 0; @@ -812,11 +670,13 @@ static void gl_draw_start(void) font_start(); - for(i = 0; i < MAX_FONTS; ++i) - LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i]); + // load default font textures + for(i = 0; i < dp_fonts.maxsize; ++i) + if (dp_fonts.f[i].title[0]) + LoadFont(false, va(vabuf, sizeof(vabuf), "gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0); // draw the loading screen so people have something to see in the newly opened window - SCR_UpdateLoadingScreen(true); + SCR_UpdateLoadingScreen(true, true); } static void gl_draw_shutdown(void) @@ -831,20 +691,44 @@ static void gl_draw_shutdown(void) static void gl_draw_newmap(void) { + int i; font_newmap(); + + // mark all of the persistent pics so they are not purged... + for (i = 0; i < numcachepics; i++) + { + cachepic_t *pic = cachepics + i; + if (!pic->autoload && pic->skinframe) + R_SkinFrame_MarkUsed(pic->skinframe); + } } void GL_Draw_Init (void) { int i, j; + + Cvar_RegisterVariable(&r_font_postprocess_blur); + Cvar_RegisterVariable(&r_font_postprocess_outline); + Cvar_RegisterVariable(&r_font_postprocess_shadow_x); + Cvar_RegisterVariable(&r_font_postprocess_shadow_y); + Cvar_RegisterVariable(&r_font_postprocess_shadow_z); + Cvar_RegisterVariable(&r_font_hinting); + Cvar_RegisterVariable(&r_font_antialias); Cvar_RegisterVariable(&r_textshadow); Cvar_RegisterVariable(&r_textbrightness); Cvar_RegisterVariable(&r_textcontrast); - Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions"); - R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap); + Cvar_RegisterVariable(&r_nearest_2d); + Cvar_RegisterVariable(&r_nearest_conchars); + // allocate fonts storage + fonts_mempool = Mem_AllocPool("FONTS", 0, NULL); + dp_fonts.maxsize = MAX_FONTS; + dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize); + memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize); + + // assign starting font names strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title)); - strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath)); + strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath)); strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title)); strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title)); strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title)); @@ -853,76 +737,90 @@ void GL_Draw_Init (void) strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title)); strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title)); for(i = 0, j = 0; i < MAX_USERFONTS; ++i) - if(!FONT_USER[i].title[0]) - dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++); - Font_Init(); + if(!FONT_USER(i)->title[0]) + dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++); + + Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions"); + R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL); } -void _DrawQ_Setup(void) +static void _DrawQ_Setup(void) // see R_ResetViewRendering2D { - r_viewport_t viewport; - if (r_refdef.draw2dstage) + if (r_refdef.draw2dstage == 1) return; - r_refdef.draw2dstage = true; - CHECKGLERROR - 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); - R_SetViewport(&viewport); - GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1); - qglDepthFunc(GL_LEQUAL);CHECKGLERROR - qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR - GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces - R_EntityMatrix(&identitymatrix); - - GL_DepthMask(true); - GL_DepthRange(0, 1); - GL_PolygonOffset(0, 0); - GL_DepthTest(false); - GL_Color(1,1,1,1); - GL_AlphaTest(false); - GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + r_refdef.draw2dstage = 1; + + R_ResetViewRendering2D_Common(0, NULL, NULL, vid_conwidth.integer, vid_conheight.integer); } -static void _DrawQ_ProcessDrawFlag(int flags) +qboolean r_draw2d_force = false; +static void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha) { _DrawQ_Setup(); - CHECKGLERROR + if(!r_draw2d.integer && !r_draw2d_force) + return; + DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->skinframe && pic->skinframe->hasalpha)); +} +void DrawQ_ProcessDrawFlag(int flags, qboolean alpha) +{ if(flags == DRAWFLAG_ADDITIVE) - GL_BlendFunc(GL_SRC_ALPHA, GL_ONE); + { + GL_DepthMask(false); + GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE); + } else if(flags == DRAWFLAG_MODULATE) + { + GL_DepthMask(false); GL_BlendFunc(GL_DST_COLOR, GL_ZERO); + } else if(flags == DRAWFLAG_2XMODULATE) - GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR); + { + GL_DepthMask(false); + GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR); + } else if(flags == DRAWFLAG_SCREEN) - GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE); - else + { + GL_DepthMask(false); + GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE); + } + else if(alpha) + { + GL_DepthMask(false); GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + else + { + GL_DepthMask(true); + GL_BlendFunc(GL_ONE, GL_ZERO); + } } void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags) { - float floats[20]; + float floats[36]; - _DrawQ_ProcessDrawFlag(flags); - GL_Color(red, green, blue, alpha); + _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha); + if(!r_draw2d.integer && !r_draw2d_force) + return; - R_Mesh_VertexPointer(floats, 0, 0); - R_Mesh_ColorPointer(NULL, 0, 0); - R_Mesh_ResetTextureState(); +// R_Mesh_ResetTextureState(); + floats[12] = 0.0f;floats[13] = 0.0f; + floats[14] = 1.0f;floats[15] = 0.0f; + floats[16] = 1.0f;floats[17] = 1.0f; + floats[18] = 0.0f;floats[19] = 1.0f; + floats[20] = floats[24] = floats[28] = floats[32] = red; + floats[21] = floats[25] = floats[29] = floats[33] = green; + floats[22] = floats[26] = floats[30] = floats[34] = blue; + floats[23] = floats[27] = floats[31] = floats[35] = alpha; if (pic) { if (width == 0) width = pic->width; if (height == 0) height = pic->height; - R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1); - R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0); - -#if 1 - floats[12] = 0.0f;floats[13] = 0.0f; - floats[14] = 1.0f;floats[15] = 0.0f; - floats[16] = 1.0f;floats[17] = 1.0f; - floats[18] = 0.0f;floats[19] = 1.0f; -#else + R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false); + +#if 0 // AK07: lets be texel correct on the corners { float horz_offset = 0.5f / pic->width; @@ -936,7 +834,7 @@ void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, flo #endif } else - R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1); + R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true); floats[2] = floats[5] = floats[8] = floats[11] = 0; floats[0] = floats[9] = x; @@ -944,12 +842,13 @@ void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, flo floats[3] = floats[6] = x + width; floats[7] = floats[10] = y + height; - R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0); + R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12); + R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0); } 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) { - float floats[20]; + float floats[36]; float af = DEG2RAD(-angle); // forward float ar = DEG2RAD(-angle + 90); // right float sinaf = sin(af); @@ -957,28 +856,21 @@ void DrawQ_RotPic(float x, float y, cachepic_t *pic, float width, float height, float sinar = sin(ar); float cosar = cos(ar); - _DrawQ_ProcessDrawFlag(flags); - GL_Color(red, green, blue, alpha); + _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha); + if(!r_draw2d.integer && !r_draw2d_force) + return; - R_Mesh_VertexPointer(floats, 0, 0); - R_Mesh_ColorPointer(NULL, 0, 0); - R_Mesh_ResetTextureState(); +// R_Mesh_ResetTextureState(); if (pic) { if (width == 0) width = pic->width; if (height == 0) height = pic->height; - R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1); - R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0); - - floats[12] = 0.0f;floats[13] = 0.0f; - floats[14] = 1.0f;floats[15] = 0.0f; - floats[16] = 1.0f;floats[17] = 1.0f; - floats[18] = 0.0f;floats[19] = 1.0f; + R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false); } else - R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1); + R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true); floats[2] = floats[5] = floats[8] = floats[11] = 0; @@ -998,28 +890,46 @@ void DrawQ_RotPic(float x, float y, cachepic_t *pic, float width, float height, floats[9] = x - cosaf*org_x + cosar*(height-org_y); floats[10] = y - sinaf*org_x + sinar*(height-org_y); - R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0); + floats[12] = 0.0f;floats[13] = 0.0f; + floats[14] = 1.0f;floats[15] = 0.0f; + floats[16] = 1.0f;floats[17] = 1.0f; + floats[18] = 0.0f;floats[19] = 1.0f; + floats[20] = floats[24] = floats[28] = floats[32] = red; + floats[21] = floats[25] = floats[29] = floats[33] = green; + floats[22] = floats[26] = floats[30] = floats[34] = blue; + floats[23] = floats[27] = floats[31] = floats[35] = alpha; + + R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12); + R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0); } void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags) { - float floats[12]; + float floats[36]; - _DrawQ_ProcessDrawFlag(flags); - GL_Color(red, green, blue, alpha); + _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha); + if(!r_draw2d.integer && !r_draw2d_force) + return; - R_Mesh_VertexPointer(floats, 0, 0); - R_Mesh_ColorPointer(NULL, 0, 0); - R_Mesh_ResetTextureState(); - R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1); +// R_Mesh_ResetTextureState(); + R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true); floats[2] = floats[5] = floats[8] = floats[11] = 0; floats[0] = floats[9] = x; floats[1] = floats[4] = y; floats[3] = floats[6] = x + width; floats[7] = floats[10] = y + height; - - R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0); + floats[12] = 0.0f;floats[13] = 0.0f; + floats[14] = 1.0f;floats[15] = 0.0f; + floats[16] = 1.0f;floats[17] = 1.0f; + floats[18] = 0.0f;floats[19] = 1.0f; + floats[20] = floats[24] = floats[28] = floats[32] = red; + floats[21] = floats[25] = floats[29] = floats[33] = green; + floats[22] = floats[26] = floats[30] = floats[34] = blue; + floats[23] = floats[27] = floats[31] = floats[35] = alpha; + + R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12); + R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0); } /// color tag printing @@ -1073,7 +983,8 @@ static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, } } -float DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(const char *text, float w, float h, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth) +// NOTE: this function always draws exactly one character if maxwidth <= 0 +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) { const char *text_start = text; int colorindex = STRING_COLOR_DEFAULT; @@ -1087,10 +998,14 @@ float DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(const char *text, float w size_t bytes_left; ft2_font_map_t *fontmap = NULL; ft2_font_map_t *map = NULL; - ft2_font_map_t *prevmap = NULL; + //ft2_font_map_t *prevmap = NULL; ft2_font_t *ft2 = fnt->ft2; // float ftbase_x; qboolean snap = true; + qboolean least_one = false; + float dw; // display w + //float dh; // display h + const float *width_of; if (!h) h = w; if (!h) { @@ -1098,8 +1013,8 @@ float DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(const char *text, float w snap = false; } // do this in the end - w *= fnt->scale; - h *= fnt->scale; + w *= fnt->settings.scale; + h *= fnt->settings.scale; // find the most fitting size: if (ft2 != NULL) @@ -1111,6 +1026,9 @@ float DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(const char *text, float w fontmap = Font_MapForIndex(ft2, map_index); } + dw = w * sw; + //dh = h * sh; + if (*maxlen < 1) *maxlen = 1<<30; @@ -1121,26 +1039,43 @@ float DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(const char *text, float w // maxwidth /= fnt->scale; // w and h are multiplied by it already // ftbase_x = snap_to_pixel_x(0); + + if(maxwidth <= 0) + { + least_one = true; + maxwidth = -maxwidth; + } + + //if (snap) + // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway - for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;) + if (fontmap) + width_of = fontmap->width_of; + else + width_of = fnt->width_of; + + i = 0; + while (((bytes_left = *maxlen - (text - text_start)) > 0) && *text) { + size_t i0 = i; nextch = ch = u8_getnchar(text, &text, bytes_left); - //i = text - text_start; + i = text - text_start; if (!ch) break; - if (snap) - x = snap_to_pixel_x(x, 0.4); if (ch == ' ' && !fontmap) { - if(x + fnt->width_of[(int) ' '] * w > maxwidth) + if(!least_one || i0) // never skip the first character + if(x + width_of[(int) ' '] * dw > maxwidth) + { + i = i0; break; // oops, can't draw this - x += fnt->width_of[(int) ' '] * w; - ++i; + } + x += width_of[(int) ' '] * dw; continue; } - if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen) + // i points to the char after ^ + if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen) { - ++i; ch = *text; // colors are ascii, so no u8_ needed if (ch <= '9' && ch >= '0') // ^[0-9] found { @@ -1149,6 +1084,9 @@ float DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(const char *text, float w ++i; continue; } + // i points to the char after ^... + // i+3 points to 3 in ^x123 + // i+3 == *maxlen would mean that char is missing else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found { // building colorindex... @@ -1188,7 +1126,6 @@ float DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(const char *text, float w i--; } ch = nextch; - ++i; if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF)) { @@ -1199,11 +1136,15 @@ float DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(const char *text, float w if (fontmap) map = ft2_oldstyle_map; prevch = 0; - if(x + fnt->width_of[ch] * w > maxwidth) + if(!least_one || i0) // never skip the first character + if(x + width_of[ch] * dw > maxwidth) + { + i = i0; break; // oops, can't draw this - x += fnt->width_of[ch] * w; + } + x += width_of[ch] * dw; } else { - if (!map || map == ft2_oldstyle_map || map->start < ch || map->start + FONT_CHARS_PER_MAP >= ch) + if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP) { map = FontMap_FindForChar(fontmap, ch); if (!map) @@ -1216,9 +1157,9 @@ float DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(const char *text, float w } mapch = ch - map->start; if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL)) - x += kx * w; - x += map->glyphs[mapch].advance_x * w; - prevmap = map; + x += kx * dw; + x += map->glyphs[mapch].advance_x * dw; + //prevmap = map; prevch = ch; } } @@ -1231,13 +1172,13 @@ float DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(const char *text, float w return x; } -float DrawQ_String_Font(float startx, float starty, const char *text, size_t maxlen, float w, float h, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt) +float DrawQ_Color[4]; +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) { int shadow, colorindex = STRING_COLOR_DEFAULT; size_t i; float x = startx, y, s, t, u, v, thisw; float *av, *at, *ac; - float color[4]; int batchcount; static float vertex3f[QUADELEMENTS_MAXQUADS*4*3]; static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2]; @@ -1246,7 +1187,7 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max Uchar prevch = 0; // used for kerning int tempcolorindex; int map_index = 0; - ft2_font_map_t *prevmap = NULL; // the previous map + //ft2_font_map_t *prevmap = NULL; // the previous map ft2_font_map_t *map = NULL; // the currently used map ft2_font_map_t *fontmap = NULL; // the font map for the size float ftbase_y; @@ -1256,10 +1197,12 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max qboolean snap = true; float pix_x, pix_y; size_t bytes_left; + float dw, dh; + const float *width_of; int tw, th; - tw = R_TextureWidth(fnt->tex); - th = R_TextureHeight(fnt->tex); + tw = Draw_GetPicWidth(fnt->pic); + th = Draw_GetPicHeight(fnt->pic); if (!h) h = w; if (!h) { @@ -1267,9 +1210,9 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max snap = false; } - starty -= (fnt->scale - 1) * h * 0.5; // center - w *= fnt->scale; - h *= fnt->scale; + starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset + w *= fnt->settings.scale; + h *= fnt->settings.scale; if (ft2 != NULL) { @@ -1280,22 +1223,24 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max fontmap = Font_MapForIndex(ft2, map_index); } + dw = w * sw; + dh = h * sh; + // draw the font at its baseline when using freetype //ftbase_x = 0; - ftbase_y = h * (4.5/6.0); + ftbase_y = dh * (4.5/6.0); if (maxlen < 1) maxlen = 1<<30; - _DrawQ_ProcessDrawFlag(flags); + _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0); + if(!r_draw2d.integer && !r_draw2d_force) + return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000); - R_Mesh_ColorPointer(color4f, 0, 0); - R_Mesh_ResetTextureState(); +// R_Mesh_ResetTextureState(); if (!fontmap) - R_Mesh_TexBind(0, R_GetTexture(fnt->tex)); - R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0); - R_Mesh_VertexPointer(vertex3f, 0, 0); - R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1); + R_Mesh_TexBind(0, Draw_GetPicTexture(fnt->pic)); + R_SetupShader_Generic(Draw_GetPicTexture(fnt->pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false); ac = color4f; at = texcoord2f; @@ -1303,12 +1248,24 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max batchcount = 0; //ftbase_x = snap_to_pixel_x(ftbase_x); - ftbase_y = snap_to_pixel_y(ftbase_y, 0.3); + if(snap) + { + startx = snap_to_pixel_x(startx, 0.4); + starty = snap_to_pixel_y(starty, 0.4); + ftbase_y = snap_to_pixel_y(ftbase_y, 0.3); + } pix_x = vid.width / vid_conwidth.value; pix_y = vid.height / vid_conheight.value; + + if (fontmap) + width_of = fontmap->width_of; + else + width_of = fnt->width_of; + for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--) { + prevch = 0; text = text_start; if (!outcolor || *outcolor == -1) @@ -1316,7 +1273,7 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max else colorindex = *outcolor; - DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0); + DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0); x = startx; y = starty; @@ -1327,31 +1284,24 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max y += r_textshadow.value * vid.height / vid_conheight.value; } */ - for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;) + while (((bytes_left = maxlen - (text - text_start)) > 0) && *text) { nextch = ch = u8_getnchar(text, &text, bytes_left); - //i = text - text_start; + i = text - text_start; if (!ch) break; - if (snap) - { - x = snap_to_pixel_x(x, 0.4); - y = snap_to_pixel_y(y, 0.4); - } if (ch == ' ' && !fontmap) { - x += fnt->width_of[(int) ' '] * w; - ++i; + x += width_of[(int) ' '] * dw; continue; } - if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen) + if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen) { - ++i; ch = *text; // colors are ascii, so no u8_ needed if (ch <= '9' && ch >= '0') // ^[0-9] found { colorindex = ch - '0'; - DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0); + DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0); ++text; ++i; continue; @@ -1381,7 +1331,7 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max colorindex = tempcolorindex | 0xf; // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa) //Con_Printf("^1colorindex:^7 %x\n", colorindex); - DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0); + DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0); i+=4; text+=4; continue; @@ -1398,21 +1348,20 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max } // get the backup ch = nextch; - ++i; // using a value of -1 for the oldstyle map because NULL means uninitialized... // this way we don't need to rebind fnt->tex for every old-style character // E000..E0FF: emulate old-font characters (to still have smileys and such available) if (shadow) { - x += pix_x * r_textshadow.value; - y += pix_y * r_textshadow.value; + x += 1.0/pix_x * r_textshadow.value; + y += 1.0/pix_y * r_textshadow.value; } if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF)) { - if (ch > 0xE000) + if (ch >= 0xE000) ch -= 0xE000; if (ch > 0xFF) - continue; + goto out; if (fontmap) { if (map != ft2_oldstyle_map) @@ -1420,15 +1369,14 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max if (batchcount) { // switching from freetype to non-freetype rendering - GL_LockArrays(0, batchcount * 4); - R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0); - GL_LockArrays(0, 0); + R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f); + R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0); batchcount = 0; ac = color4f; at = texcoord2f; av = vertex3f; } - R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1); + R_SetupShader_Generic(Draw_GetPicTexture(fnt->pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false); map = ft2_oldstyle_map; } } @@ -1437,47 +1385,55 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max //thisw = fnt->width_of[num]; thisw = fnt->width_of[ch]; // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering - s = (ch & 15)*0.0625f + (0.5f / tw); - t = (ch >> 4)*0.0625f + (0.5f / th); - u = 0.0625f * thisw - (1.0f / tw); - v = 0.0625f - (1.0f / th); - ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3]; - ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3]; - ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3]; - ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3]; + if (r_nearest_conchars.integer) + { + s = (ch & 15)*0.0625f; + t = (ch >> 4)*0.0625f; + u = 0.0625f * thisw; + v = 0.0625f; + } + else + { + s = (ch & 15)*0.0625f + (0.5f / tw); + t = (ch >> 4)*0.0625f + (0.5f / th); + u = 0.0625f * thisw - (1.0f / tw); + v = 0.0625f - (1.0f / th); + } + ac[ 0] = DrawQ_Color[0];ac[ 1] = DrawQ_Color[1];ac[ 2] = DrawQ_Color[2];ac[ 3] = DrawQ_Color[3]; + ac[ 4] = DrawQ_Color[0];ac[ 5] = DrawQ_Color[1];ac[ 6] = DrawQ_Color[2];ac[ 7] = DrawQ_Color[3]; + ac[ 8] = DrawQ_Color[0];ac[ 9] = DrawQ_Color[1];ac[10] = DrawQ_Color[2];ac[11] = DrawQ_Color[3]; + ac[12] = DrawQ_Color[0];ac[13] = DrawQ_Color[1];ac[14] = DrawQ_Color[2];ac[15] = DrawQ_Color[3]; at[ 0] = s ; at[ 1] = t ; at[ 2] = s+u ; at[ 3] = t ; at[ 4] = s+u ; at[ 5] = t+v ; at[ 6] = s ; at[ 7] = t+v ; av[ 0] = x ; av[ 1] = y ; av[ 2] = 10; - av[ 3] = x+w*thisw ; av[ 4] = y ; av[ 5] = 10; - av[ 6] = x+w*thisw ; av[ 7] = y+h ; av[ 8] = 10; - av[ 9] = x ; av[10] = y+h ; av[11] = 10; + av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10; + av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10; + av[ 9] = x ; av[10] = y+dh ; av[11] = 10; ac += 16; at += 8; av += 12; batchcount++; if (batchcount >= QUADELEMENTS_MAXQUADS) { - GL_LockArrays(0, batchcount * 4); - R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0); - GL_LockArrays(0, 0); + R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f); + R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0); batchcount = 0; ac = color4f; at = texcoord2f; av = vertex3f; } - x += thisw * w; + x += width_of[ch] * dw; } else { - if (!map || map == ft2_oldstyle_map || map->start < ch || map->start + FONT_CHARS_PER_MAP >= ch) + if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP) { // new charmap - need to render if (batchcount) { // we need a different character map, render what we currently have: - GL_LockArrays(0, batchcount * 4); - R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0); - GL_LockArrays(0, 0); + R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f); + R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0); batchcount = 0; ac = color4f; at = texcoord2f; @@ -1499,7 +1455,7 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max break; } } - R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1); + R_SetupShader_Generic(Draw_GetPicTexture(map->pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false); } mapch = ch - map->start; @@ -1509,94 +1465,83 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max y += ftbase_y; if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky)) { - x += kx * w; - y += ky * h; + x += kx * dw; + y += ky * dh; } else kx = ky = 0; - ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3]; - ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3]; - ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3]; - ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3]; + ac[ 0] = DrawQ_Color[0]; ac[ 1] = DrawQ_Color[1]; ac[ 2] = DrawQ_Color[2]; ac[ 3] = DrawQ_Color[3]; + ac[ 4] = DrawQ_Color[0]; ac[ 5] = DrawQ_Color[1]; ac[ 6] = DrawQ_Color[2]; ac[ 7] = DrawQ_Color[3]; + ac[ 8] = DrawQ_Color[0]; ac[ 9] = DrawQ_Color[1]; ac[10] = DrawQ_Color[2]; ac[11] = DrawQ_Color[3]; + ac[12] = DrawQ_Color[0]; ac[13] = DrawQ_Color[1]; ac[14] = DrawQ_Color[2]; ac[15] = DrawQ_Color[3]; at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin; at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin; at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax; at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax; - av[ 0] = x + w * map->glyphs[mapch].vxmin; av[ 1] = y + h * map->glyphs[mapch].vymin; av[ 2] = 10; - av[ 3] = x + w * map->glyphs[mapch].vxmax; av[ 4] = y + h * map->glyphs[mapch].vymin; av[ 5] = 10; - av[ 6] = x + w * map->glyphs[mapch].vxmax; av[ 7] = y + h * map->glyphs[mapch].vymax; av[ 8] = 10; - av[ 9] = x + w * map->glyphs[mapch].vxmin; av[10] = y + h * map->glyphs[mapch].vymax; av[11] = 10; + av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10; + av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10; + av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10; + av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10; //x -= ftbase_x; y -= ftbase_y; - x += thisw * w; + x += thisw * dw; ac += 16; at += 8; av += 12; batchcount++; if (batchcount >= QUADELEMENTS_MAXQUADS) { - GL_LockArrays(0, batchcount * 4); - R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0); - GL_LockArrays(0, 0); + R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f); + R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0); batchcount = 0; ac = color4f; at = texcoord2f; av = vertex3f; } - prevmap = map; + //prevmap = map; prevch = ch; } +out: if (shadow) { - x -= pix_x * r_textshadow.value; - y -= pix_y * r_textshadow.value; + x -= 1.0/pix_x * r_textshadow.value; + y -= 1.0/pix_y * r_textshadow.value; } } } if (batchcount > 0) { - GL_LockArrays(0, batchcount * 4); - R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0); - GL_LockArrays(0, 0); + R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f); + R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0); } if (outcolor) *outcolor = colorindex; - + // note: this relies on the proper text (not shadow) being drawn last return x; } -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) +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) { - return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]); + return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt); } -float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt) +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) { - return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, ignorecolorcodes, fnt, 1000000000); + return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth); } -float DrawQ_TextWidth_Font_Size(const char *text, float w, float h, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt) +float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt) { - return DrawQ_TextWidth_Font_UntilWidth_Size(text, w, h, &maxlen, ignorecolorcodes, fnt, 1000000000); + return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000); } -float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth) +float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth) { - return DrawQ_TextWidth_Font_UntilWidth_TrackColors(text, maxlen, NULL, ignorecolorcodes, fnt, maxWidth); -} - -float DrawQ_TextWidth_Font_UntilWidth_Size(const char *text, float w, float h, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth) -{ - return DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(text, w, h, maxlen, NULL, ignorecolorcodes, fnt, maxWidth); -} - -float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth) -{ - return DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(text, 0, 0, maxlen, outcolor, ignorecolorcodes, fnt, maxwidth); + return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth); } #if 0 @@ -1643,91 +1588,187 @@ void DrawQ_SuperPic(float x, float y, cachepic_t *pic, float width, float height { float floats[36]; - _DrawQ_ProcessDrawFlag(flags); + _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4); + if(!r_draw2d.integer && !r_draw2d_force) + return; - R_Mesh_VertexPointer(floats, 0, 0); - R_Mesh_ColorPointer(floats + 20, 0, 0); - R_Mesh_ResetTextureState(); +// R_Mesh_ResetTextureState(); if (pic) { if (width == 0) width = pic->width; if (height == 0) height = pic->height; - R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1); - R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0); - floats[12] = s1;floats[13] = t1; - floats[14] = s2;floats[15] = t2; - floats[16] = s4;floats[17] = t4; - floats[18] = s3;floats[19] = t3; + R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & (DRAWFLAGS_BLEND | DRAWFLAG_NOGAMMA)) ? false : true, true, false); } else - R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1); + R_SetupShader_Generic_NoTexture((flags & (DRAWFLAGS_BLEND | DRAWFLAG_NOGAMMA)) ? false : true, true); floats[2] = floats[5] = floats[8] = floats[11] = 0; floats[0] = floats[9] = x; floats[1] = floats[4] = y; floats[3] = floats[6] = x + width; floats[7] = floats[10] = y + height; + floats[12] = s1;floats[13] = t1; + floats[14] = s2;floats[15] = t2; + floats[16] = s4;floats[17] = t4; + floats[18] = s3;floats[19] = t3; floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1; floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2; floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4; floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3; - R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0); + R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12); + R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0); } -void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags) +void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha) { - _DrawQ_ProcessDrawFlag(flags); + _DrawQ_Setup(); + if(!r_draw2d.integer && !r_draw2d_force) + return; + DrawQ_ProcessDrawFlag(flags, hasalpha); - R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0); - R_Mesh_ColorPointer(mesh->data_color4f, 0, 0); - R_Mesh_ResetTextureState(); - R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0); - R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1); +// R_Mesh_ResetTextureState(); + R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false); - GL_LockArrays(0, mesh->num_vertices); - R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, mesh->data_element3s, 0, 0); - GL_LockArrays(0, 0); + R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f); + R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0); } void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags) { - int num; - - _DrawQ_ProcessDrawFlag(flags); + _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1); + if(!r_draw2d.integer && !r_draw2d_force) + return; GL_Color(1,1,1,1); - CHECKGLERROR - qglBegin(GL_LINE_LOOP); - for (num = 0;num < mesh->num_vertices;num++) + switch(vid.renderpath) { - if (mesh->data_color4f) - 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]); - qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]); + case RENDERPATH_GL11: + case RENDERPATH_GL13: + case RENDERPATH_GL20: +#ifndef USE_GLES2 + { + int num; + CHECKGLERROR + qglBegin(GL_LINE_LOOP); + for (num = 0;num < mesh->num_vertices;num++) + { + if (mesh->data_color4f) + 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]); + qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]); + } + qglEnd(); + CHECKGLERROR + } +#endif + break; + case RENDERPATH_D3D9: + //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); + break; + case RENDERPATH_D3D10: + Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); + break; + case RENDERPATH_D3D11: + Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); + break; + case RENDERPATH_SOFT: + //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); + break; + case RENDERPATH_GLES1: + case RENDERPATH_GLES2: + //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); + return; } - qglEnd(); - CHECKGLERROR } //[515]: this is old, delete void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags) { - _DrawQ_ProcessDrawFlag(flags); + _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha); + if(!r_draw2d.integer && !r_draw2d_force) + return; + + R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true); + + switch(vid.renderpath) + { + case RENDERPATH_GL11: + case RENDERPATH_GL13: + case RENDERPATH_GL20: +#ifndef USE_GLES2 + CHECKGLERROR + + //qglLineWidth(width);CHECKGLERROR + + GL_Color(r,g,b,alpha); + CHECKGLERROR + qglBegin(GL_LINES); + qglVertex2f(x1, y1); + qglVertex2f(x2, y2); + qglEnd(); + CHECKGLERROR +#endif + break; + case RENDERPATH_D3D9: + //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); + break; + case RENDERPATH_D3D10: + Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); + break; + case RENDERPATH_D3D11: + Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); + break; + case RENDERPATH_SOFT: + //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); + break; + case RENDERPATH_GLES1: + case RENDERPATH_GLES2: + //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); + return; + } +} + +void DrawQ_Lines (float width, int numlines, int flags, qboolean hasalpha) +{ + _DrawQ_SetupAndProcessDrawFlag(flags, NULL, hasalpha ? 0.5f : 1.0f); - R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1); + if(!r_draw2d.integer && !r_draw2d_force) + return; - CHECKGLERROR - //qglLineWidth(width);CHECKGLERROR + switch(vid.renderpath) + { + case RENDERPATH_GL11: + case RENDERPATH_GL13: + case RENDERPATH_GL20: + CHECKGLERROR - GL_Color(r,g,b,alpha); - CHECKGLERROR - qglBegin(GL_LINES); - qglVertex2f(x1, y1); - qglVertex2f(x2, y2); - qglEnd(); - CHECKGLERROR + R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true); + + //qglLineWidth(width);CHECKGLERROR + + CHECKGLERROR + qglDrawArrays(GL_LINES, 0, numlines*2); + CHECKGLERROR + break; + case RENDERPATH_D3D9: + //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); + break; + case RENDERPATH_D3D10: + Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); + break; + case RENDERPATH_D3D11: + Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); + break; + case RENDERPATH_SOFT: + //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); + break; + case RENDERPATH_GLES1: + case RENDERPATH_GLES2: + //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); + return; + } } void DrawQ_SetClipArea(float x, float y, float width, float height) @@ -1736,12 +1777,31 @@ void DrawQ_SetClipArea(float x, float y, float width, float height) _DrawQ_Setup(); // We have to convert the con coords into real coords - // OGL uses top to bottom - ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer)); - iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer)); - iw = (int)(width * ((float)vid.width / vid_conwidth.integer)); - ih = (int)(height * ((float)vid.height / vid_conheight.integer)); - GL_Scissor(ix, vid.height - iy - ih, iw, ih); + // OGL uses bottom to top (origin is in bottom left) + ix = (int)(0.5 + x * ((float)r_refdef.view.width / vid_conwidth.integer)) + r_refdef.view.x; + iy = (int)(0.5 + y * ((float)r_refdef.view.height / vid_conheight.integer)) + r_refdef.view.y; + iw = (int)(0.5 + width * ((float)r_refdef.view.width / vid_conwidth.integer)); + ih = (int)(0.5 + height * ((float)r_refdef.view.height / vid_conheight.integer)); + switch(vid.renderpath) + { + case RENDERPATH_GL11: + case RENDERPATH_GL13: + case RENDERPATH_GL20: + case RENDERPATH_GLES1: + case RENDERPATH_GLES2: + case RENDERPATH_SOFT: + GL_Scissor(ix, vid.height - iy - ih, iw, ih); + break; + case RENDERPATH_D3D9: + GL_Scissor(ix, iy, iw, ih); + break; + case RENDERPATH_D3D10: + Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); + break; + case RENDERPATH_D3D11: + Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__); + break; + } GL_ScissorTest(true); } @@ -1754,66 +1814,12 @@ void DrawQ_ResetClipArea(void) void DrawQ_Finish(void) { - r_refdef.draw2dstage = false; + r_refdef.draw2dstage = 0; } -static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10}; -void R_DrawGamma(void) +void DrawQ_RecalcView(void) { - float c[4]; - switch(vid.renderpath) - { - case RENDERPATH_GL20: - case RENDERPATH_CGGL: - if (vid_usinghwgamma || v_glslgamma.integer) - return; - break; - case RENDERPATH_GL13: - case RENDERPATH_GL11: - if (vid_usinghwgamma) - return; - break; - } - // all the blends ignore depth - R_Mesh_VertexPointer(blendvertex3f, 0, 0); - R_Mesh_ColorPointer(NULL, 0, 0); - R_Mesh_ResetTextureState(); - R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1); - GL_DepthMask(true); - GL_DepthRange(0, 1); - GL_PolygonOffset(0, 0); - GL_DepthTest(false); - if (v_color_enable.integer) - { - c[0] = v_color_white_r.value; - c[1] = v_color_white_g.value; - c[2] = v_color_white_b.value; - } - else - c[0] = c[1] = c[2] = v_contrast.value; - if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f) - { - GL_BlendFunc(GL_DST_COLOR, GL_ONE); - while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f) - { - GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1); - R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0); - VectorScale(c, 0.5, c); - } - } - if (v_color_enable.integer) - { - c[0] = v_color_black_r.value; - c[1] = v_color_black_g.value; - c[2] = v_color_black_b.value; - } - else - c[0] = c[1] = c[2] = v_brightness.value; - if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f) - { - GL_BlendFunc(GL_ONE, GL_ONE); - GL_Color(c[0], c[1], c[2], 1); - R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0); - } + if(r_refdef.draw2dstage) + r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again }