#include "quakedef.h" #ifndef SERVERONLY #include "shader.h" #include "gl_draw.h" #define WIN32_BLOATED #include "glquake.h" qboolean r2d_canhwgamma; //says the video code has successfully activated hardware gamma texid_t missing_texture; texid_t missing_texture_gloss; texid_t missing_texture_normal; texid_t translate_texture; shader_t *translate_shader; texid_t ch_int_texture; vec3_t ch_color; shader_t *shader_crosshair; static mpic_t *conback; static mpic_t *draw_backtile; shader_t *shader_draw_fill, *shader_draw_fill_trans; mpic_t *draw_disc; shader_t *shader_contrastup; shader_t *shader_contrastdown; shader_t *shader_brightness; shader_t *shader_gammacb; shader_t *shader_polyblend; shader_t *shader_menutint; #define DRAW_QUADS 128 static int draw_active_flags; static shader_t *draw_active_shader; static avec4_t draw_active_colour; static mesh_t draw_mesh; static vecV_t draw_mesh_xyz[DRAW_QUADS]; vec2_t draw_mesh_st[DRAW_QUADS]; static avec4_t draw_mesh_colors[DRAW_QUADS]; index_t r_quad_indexes[DRAW_QUADS*6]; unsigned int r2d_be_flags; struct { lmalloc_t allocation; qboolean dirty; uploadfmt_t fmt; int lastid; unsigned int *data; shader_t *shader; texid_t tex; apic_t *pics; } atlas; extern cvar_t scr_conalpha; extern cvar_t gl_conback; extern cvar_t gl_font, con_textfont; extern cvar_t r_font_postprocess_outline, r_font_postprocess_mono; extern cvar_t gl_screenangle; extern cvar_t vid_minsize; extern cvar_t vid_conautoscale; extern cvar_t vid_baseheight; extern cvar_t vid_conheight; extern cvar_t vid_conwidth; extern cvar_t con_textsize; static void QDECL R2D_Font_Callback(struct cvar_s *var, char *oldvalue); static void QDECL R2D_Conautoscale_Callback(struct cvar_s *var, char *oldvalue); static void QDECL R2D_ScreenAngle_Callback(struct cvar_s *var, char *oldvalue); static void QDECL R2D_Conheight_Callback(struct cvar_s *var, char *oldvalue); static void QDECL R2D_Conwidth_Callback(struct cvar_s *var, char *oldvalue); extern cvar_t crosshair; extern cvar_t crosshaircolor; extern cvar_t crosshairsize; extern cvar_t crosshairimage; extern cvar_t crosshairalpha; static void QDECL R2D_Crosshair_Callback(struct cvar_s *var, char *oldvalue); static void QDECL R2D_CrosshairImage_Callback(struct cvar_s *var, char *oldvalue); static void QDECL R2D_CrosshairColor_Callback(struct cvar_s *var, char *oldvalue); void (*R2D_Flush)(void); //We need this for minor things though, so we'll just use the slow accurate method. //this is unlikly to be called too often. qbyte GetPaletteIndex(int red, int green, int blue) { //slow, horrible method. { int i, best=15; int bestdif=256*256*256, curdif; extern qbyte *host_basepal; qbyte *pa; #define _abs(x) ((x)*(x)) pa = host_basepal; for (i = 0; i < 256; i++, pa+=3) { curdif = _abs(red - pa[0]) + _abs(green - pa[1]) + _abs(blue - pa[2]); if (curdif < bestdif) { if (curdif<1) return i; bestdif = curdif; best = i; } } return best; } } qbyte GetPaletteIndexNoFB(int red, int green, int blue) { //slow, horrible method. { int i, best=15; int bestdif=256*256*256, curdif; extern qbyte *host_basepal; qbyte *pa; #define _abs(x) ((x)*(x)) pa = host_basepal; for (i = 0; i < 256 - vid.fullbright; i++, pa+=3) { curdif = _abs(red - pa[0]) + _abs(green - pa[1]) + _abs(blue - pa[2]); if (curdif < bestdif) { if (curdif<1) return i; bestdif = curdif; best = i; } } return best; } } qbyte GetPaletteIndexRange(int first, int stop, int red, int green, int blue) { //slow, horrible method. { int i, best=15; int bestdif=256*256*256, curdif; extern qbyte *host_basepal; qbyte *pa; #define _abs(x) ((x)*(x)) pa = host_basepal; for (i = first; i < stop; i++, pa+=3) { curdif = _abs(red - pa[0]) + _abs(green - pa[1]) + _abs(blue - pa[2]); if (curdif < bestdif) { if (curdif<1) return i; bestdif = curdif; best = i; } } return best; } } void R2D_Shutdown(void) { Cvar_Unhook(&con_textfont); Cvar_Unhook(&gl_font); Cvar_Unhook(&r_font_postprocess_outline); Cvar_Unhook(&r_font_postprocess_mono); Cvar_Unhook(&vid_conautoscale); Cvar_Unhook(&gl_screenangle); Cvar_Unhook(&vid_conheight); Cvar_Unhook(&vid_conwidth); Cvar_Unhook(&vid_baseheight); Cvar_Unhook(&vid_minsize); Cvar_Unhook(&crosshair); Cvar_Unhook(&crosshairimage); Cvar_Unhook(&crosshaircolor); BZ_Free(cl_stris); cl_stris = NULL; BZ_Free(cl_strisvertv); cl_strisvertv = NULL; BZ_Free(cl_strisvertc); cl_strisvertc = NULL; BZ_Free(cl_strisvertt); cl_strisvertt = NULL; BZ_Free(cl_strisidx); cl_strisidx = NULL; cl_numstrisidx = 0; cl_maxstrisidx = 0; cl_numstrisvert = 0; cl_maxstrisvert = 0; cl_numstris = 0; cl_maxstris = 0; Con_FlushBackgrounds(); if (font_console == font_default) font_console = NULL; if (font_console) Font_Free(font_console); font_console = NULL; if (font_default) Font_Free(font_default); font_default = NULL; if (font_tiny) Font_Free(font_tiny); font_tiny = NULL; #ifndef NOBUILTINMENUS M_ReloadMenus(); #endif #if defined(MENU_DAT) || defined(CSQC_DAT) PR_ReloadFonts(false); #endif while(atlas.pics) { apic_t *a = atlas.pics; atlas.pics = a->next; Z_Free(a); } Z_Free(atlas.data); memset(&atlas, 0, sizeof(atlas)); } /* Iniitalise the 2d rendering functions (including font). Image loading code must be ready for use at this point. */ void R2D_Init(void) { unsigned int nonorm[4*4]; unsigned int nogloss[4*4]; int i, j; unsigned int glossval; unsigned int normval; extern cvar_t gl_specular_fallback, gl_specular_fallbackexp, gl_texturemode; conback = NULL; Cvar_ForceCallback(&gl_texturemode); draw_mesh.istrifan = true; draw_mesh.numvertexes = 4; draw_mesh.numindexes = 6; draw_mesh.xyz_array = draw_mesh_xyz; draw_mesh.st_array = draw_mesh_st; draw_mesh.colors4f_array[0] = draw_mesh_colors; draw_mesh.indexes = r_quad_indexes; for (i = 0, j = 0; i < countof(r_quad_indexes); i += 6, j += 4) { r_quad_indexes[i+0] = j+0; r_quad_indexes[i+1] = j+1; r_quad_indexes[i+2] = j+2; r_quad_indexes[i+3] = j+2; r_quad_indexes[i+4] = j+3; r_quad_indexes[i+5] = j+0; } if (strchr(gl_specular_fallback.string, ' ')) { glossval = bound(0, (int)(gl_specular_fallback.vec4[0]*255), 255)<<0; glossval |= bound(0, (int)(gl_specular_fallback.vec4[1]*255), 255)<<8; glossval |= bound(0, (int)(gl_specular_fallback.vec4[2]*255), 255)<<16; } else { glossval = min(gl_specular_fallback.value*255, 255); glossval *= 0x10101; } glossval |= 0x01000000 * bound(0, (int)(gl_specular_fallbackexp.value*255), 255); glossval = LittleLong(glossval); normval = 0xffff8080; normval = LittleLong(normval); for (i = 0; i < 4*4; i++) { nogloss[i] = glossval; nonorm[i] = normval; } missing_texture = R_LoadTexture8("no_texture", 16, 16, (unsigned char*)(r_notexture_mip+1), IF_NOALPHA|IF_NOGAMMA|IF_NOPURGE, 0); missing_texture_gloss = R_LoadTexture("no_texture_gloss", 4, 4, TF_RGBA32, (unsigned char*)nogloss, IF_NOGAMMA|IF_NOPURGE); missing_texture_normal = R_LoadTexture("no_texture_normal", 4, 4, TF_RGBA32, (unsigned char*)nonorm, IF_NOGAMMA|IF_NOPURGE); translate_texture = r_nulltex; ch_int_texture = r_nulltex; Shader_Init(); BE_Init(); Font_Init(); draw_backtile = R_RegisterShader("gfx/backtile.lmp", SUF_NONE, "{\n" "if $nofixed\n" "program default2d\n" "endif\n" "affine\n" "nomipmaps\n" "{\n" "map $diffuse\n" "}\n" "}\n"); TEXDOWAIT(draw_backtile->defaulttextures->base); if (!TEXLOADED(draw_backtile->defaulttextures->base)) draw_backtile->defaulttextures->base = R_LoadHiResTexture("gfx/backtile", NULL, IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_NOWORKER); if (!TEXLOADED(draw_backtile->defaulttextures->base)) draw_backtile->defaulttextures->base = R_LoadHiResTexture("gfx/menu/backtile", NULL, IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_NOWORKER); if (!TEXLOADED(draw_backtile->defaulttextures->base)) draw_backtile->defaulttextures->base = R_LoadHiResTexture("pics/backtile", NULL, IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_NOWORKER); shader_draw_fill = R_RegisterShader("fill_opaque", SUF_NONE, "{\n" "program defaultfill\n" "{\n" "map $whiteimage\n" "rgbgen exactvertex\n" "alphagen vertex\n" "}\n" "}\n"); shader_draw_fill_trans = R_RegisterShader("fill_trans", SUF_NONE, "{\n" "program defaultfill\n" "{\n" "map $whiteimage\n" "rgbgen vertex\n" "alphagen vertex\n" "blendfunc blend\n" "maskalpha\n" "}\n" "}\n"); shader_contrastup = R_RegisterShader("contrastupshader", SUF_NONE, "{\n" "program defaultfill\n" "{\n" "nodepthtest\n" "map $whiteimage\n" "blendfunc gl_dst_color gl_one\n" "rgbgen vertex\n" "alphagen vertex\n" "maskalpha\n" "}\n" "}\n" ); shader_contrastdown = R_RegisterShader("contrastdownshader", SUF_NONE, "{\n" "program defaultfill\n" "{\n" "nodepthtest\n" "map $whiteimage\n" "blendfunc gl_dst_color gl_zero\n" "rgbgen vertex\n" "alphagen vertex\n" "maskalpha\n" "}\n" "}\n" ); shader_brightness = R_RegisterShader("brightnessshader", SUF_NONE, "{\n" "program defaultfill\n" "{\n" "nodepthtest\n" "map $whiteimage\n" "blendfunc gl_one gl_one\n" "rgbgen vertex\n" "alphagen vertex\n" "maskalpha\n" "}\n" "}\n" ); shader_gammacb = R_RegisterShader("gammacbshader", SUF_NONE, "{\n" "program defaultgammacb\n" "affine\n" "{\n" "map $currentrender\n" "nodepthtest\n" "maskalpha\n" "}\n" "}\n" ); shader_polyblend = R_RegisterShader("polyblendshader", SUF_NONE, "{\n" "program defaultfill\n" "{\n" "map $whiteimage\n" "blendfunc gl_src_alpha gl_one_minus_src_alpha\n" "rgbgen vertex\n" "alphagen vertex\n" "maskalpha\n" "}\n" "}\n" ); shader_menutint = R_RegisterShader("menutint", SUF_NONE, "{\n" "affine\n" #ifdef FTE_TARGET_WEB //currentrender is problematic here, so avoid using it. "program default2d\n" "{\n" "map $whiteimage\n" "blendfunc gl_dst_color gl_zero\n" "rgbgen srgb $r_menutint\n" "}\n" #else "if gl_menutint_shader != 0\n" "program menutint\n" "endif\n" "if $haveprogram\n" "{\n" "map $currentrender\n" "}\n" "else\n" "{\n" "map $whiteimage\n" "blendfunc gl_dst_color gl_zero\n" "rgbgen srgb $r_menutint\n" "}\n" "endif\n" #endif "}\n" ); shader_crosshair = R_RegisterShader("crosshairshader", SUF_NONE, "{\n" "if $nofixed\n" "program default2d\n" "endif\n" "affine\n" "nomipmaps\n" "{\n" "map $diffuse\n" "blendfunc blend\n" "rgbgen vertex\n" "alphagen vertex\n" "}\n" "}\n" ); Cvar_Hook(&con_textfont, R2D_Font_Callback); Cvar_Hook(&gl_font, R2D_Font_Callback); Cvar_Hook(&r_font_postprocess_outline, R2D_Font_Callback); Cvar_Hook(&r_font_postprocess_mono, R2D_Font_Callback); Cvar_Hook(&vid_conautoscale, R2D_Conautoscale_Callback); Cvar_Hook(&gl_screenangle, R2D_ScreenAngle_Callback); Cvar_Hook(&vid_conheight, R2D_Conheight_Callback); Cvar_Hook(&vid_conwidth, R2D_Conwidth_Callback); Cvar_Hook(&vid_baseheight, R2D_Conautoscale_Callback); Cvar_Hook(&vid_minsize, R2D_Conautoscale_Callback); Cvar_Hook(&crosshair, R2D_Crosshair_Callback); Cvar_Hook(&crosshairimage, R2D_CrosshairImage_Callback); Cvar_Hook(&crosshaircolor, R2D_CrosshairColor_Callback); Cvar_ForceCallback(&gl_conback); Cvar_ForceCallback(&vid_conautoscale); Cvar_ForceCallback(&gl_font); Cvar_ForceCallback(&crosshair); Cvar_ForceCallback(&crosshaircolor); R2D_Font_Changed(); R_NetgraphInit(); atlas.lastid = -1; atlas.shader = NULL; atlas.data = NULL; atlas.dirty = false; Mod_LightmapAllocInit(&atlas.allocation, false, min(512, sh_config.texture2d_maxsize), min(512, sh_config.texture2d_maxsize), 0); } mpic_t *R2D_SafeCachePic (const char *path) { shader_t *s; if (!qrenderer) return NULL; s = R_RegisterPic(path, NULL); return s; } mpic_t *R2D_SafePicFromWad (const char *name) { shader_t *s; if (!qrenderer) return NULL; s = R_RegisterCustom (NULL, va("gfx/%s", name), SUF_2D, Shader_Default2D, "wad"); return s; } apic_t *R2D_LoadAtlasedPic(const char *name) { apic_t *apic = Z_Malloc(sizeof(*apic)); qpic_t *qp; int x,y; qbyte *indata = NULL; int atlasid; if (!gl_load24bit.ival) { size_t lumpsize; qbyte lumptype; qp = W_GetLumpName(name, &lumpsize, &lumptype); if (qp && lumptype == TYP_QPIC && lumpsize == 8+qp->width*qp->height) { apic->width = qp->width; apic->height = qp->height; indata = qp->data; } } if (!indata || apic->width > atlas.allocation.width || apic->height > atlas.allocation.height) atlasid = -1; //can happen on voodoo cards else Mod_LightmapAllocBlock(&atlas.allocation, apic->width+2, apic->height+2, &apic->x, &apic->y, &atlasid); if (atlasid >= 0) { unsigned int *out; apic->x += 1; apic->y += 1; //FIXME: extend the atlas height instead, and keep going with a single atlas? if (atlasid != atlas.lastid) { atlas.lastid = atlasid; if (atlas.dirty) Image_Upload(atlas.tex, atlas.fmt, atlas.data, NULL, atlas.allocation.width, atlas.allocation.height, 1, IF_NOMIPMAP); atlas.tex = r_nulltex; atlas.fmt = sh_config.texfmt[PTI_BGRA8]?PTI_BGRA8:PTI_RGBA8; atlas.shader = NULL; atlas.dirty = false; if (atlas.data) //clear atlas data instead of reallocating it. memset(atlas.data, 0, sizeof(*atlas.data) * atlas.allocation.width * atlas.allocation.height); } if (!atlas.tex) atlas.tex = Image_CreateTexture(va("fte_atlas%i", atlasid), NULL, IF_NOMIPMAP|IF_NOMIPMAP); if (!atlas.shader) { atlas.shader = R_RegisterShader(va("fte_atlas%i", atlasid), SUF_NONE, "{\n" "affine\n" "program default2d\n" "{\n" "map $diffuse\n" "rgbgen vertex\n" "alphagen vertex\n" "blendfunc gl_one gl_one_minus_src_alpha\n" "}\n" "}\n" ); atlas.shader->defaulttextures->base = atlas.tex; } if (!atlas.data) atlas.data = Z_Malloc(sizeof(*atlas.data) * atlas.allocation.width * atlas.allocation.height); out = atlas.data; out += apic->x; out += apic->y * atlas.allocation.width; apic->atlas = atlas.shader; if (atlas.fmt == PTI_BGRA8) { //pad above. extra casts because 64bit msvc is RETARDED. out[-1 - (qintptr_t)atlas.allocation.width] = (indata[0] == 255)?0:d_8to24bgrtable[indata[0]]; //pad left for (x = 0; x < apic->width; x++) out[x-(qintptr_t)atlas.allocation.width] = (indata[x] == 255)?0:d_8to24bgrtable[indata[x]]; out[x - (qintptr_t)atlas.allocation.width] = (indata[x-1] == 255)?0:d_8to24bgrtable[indata[x-1]]; //pad right for (y = 0; y < apic->height; y++) { out[-1] = (indata[0] == 255)?0:d_8to24bgrtable[indata[0]]; //pad left for (x = 0; x < apic->width; x++) out[x] = (indata[x] == 255)?0:d_8to24bgrtable[indata[x]]; out[x] = (indata[x-1] == 255)?0:d_8to24bgrtable[indata[x-1]]; //pad right indata += x; out += atlas.allocation.width; } //pad below out[-1] = (indata[0] == 255)?0:d_8to24bgrtable[indata[0]]; //pad left for (x = 0; x < apic->width; x++) out[x] = (indata[x] == 255)?0:d_8to24bgrtable[indata[x]]; out[x] = (indata[x-1] == 255)?0:d_8to24bgrtable[indata[x-1]]; //pad right } else { //pad above. extra casts because 64bit msvc is RETARDED. out[-1 - (qintptr_t)atlas.allocation.width] = (indata[0] == 255)?0:d_8to24rgbtable[indata[0]]; //pad left for (x = 0; x < apic->width; x++) out[x-(qintptr_t)atlas.allocation.width] = (indata[x] == 255)?0:d_8to24rgbtable[indata[x]]; out[x - (qintptr_t)atlas.allocation.width] = (indata[x-1] == 255)?0:d_8to24rgbtable[indata[x-1]]; //pad right for (y = 0; y < apic->height; y++) { out[-1] = (indata[0] == 255)?0:d_8to24rgbtable[indata[0]]; //pad left for (x = 0; x < apic->width; x++) out[x] = (indata[x] == 255)?0:d_8to24rgbtable[indata[x]]; out[x] = (indata[x-1] == 255)?0:d_8to24rgbtable[indata[x-1]]; //pad right indata += x; out += atlas.allocation.width; } //pad below out[-1] = (indata[0] == 255)?0:d_8to24rgbtable[indata[0]]; //pad left for (x = 0; x < apic->width; x++) out[x] = (indata[x] == 255)?0:d_8to24rgbtable[indata[x]]; out[x] = (indata[x-1] == 255)?0:d_8to24rgbtable[indata[x-1]]; //pad right } //pinch inwards, for linear sampling apic->sl = (apic->x+0.5)/(float)atlas.allocation.width; apic->sh = (apic->width-1.0)/(float)atlas.allocation.width; apic->tl = (apic->y+0.5)/(float)atlas.allocation.height; apic->th = (apic->height-1.0)/(float)atlas.allocation.height; atlas.dirty = true; apic->next = atlas.pics; atlas.pics = apic; } else if (1) { apic->atlas = R_RegisterPic(va("gfx/%s", name), "wad"); apic->sl = 0; apic->sh = 1; apic->tl = 0; apic->th = 1; apic->next = atlas.pics; atlas.pics = apic; } else { Z_Free(apic); return NULL; } return apic; } void R2D_ImageColours(float r, float g, float b, float a) { draw_active_colour[0] = r; draw_active_colour[1] = g; draw_active_colour[2] = b; draw_active_colour[3] = a; Font_InvalidateColour(draw_active_colour); } void R2D_ImagePaletteColour(unsigned int i, float a) { draw_active_colour[0] = SRGBf(host_basepal[i*3+0]/255.0); draw_active_colour[1] = SRGBf(host_basepal[i*3+1]/255.0); draw_active_colour[2] = SRGBf(host_basepal[i*3+2]/255.0); draw_active_colour[3] = a; Font_InvalidateColour(draw_active_colour); } //awkward and weird to use void R2D_ImageAtlas(float x, float y, float w, float h, float s1, float t1, float s2, float t2, apic_t *pic) { float newsl, newsh, newtl, newth; if (!pic) return; if (atlas.dirty) { Image_Upload(atlas.tex, atlas.fmt, atlas.data, NULL, atlas.allocation.width, atlas.allocation.height, 1, IF_NOMIPMAP); atlas.dirty = false; } newsl = pic->sl + s1 * pic->sh; newsh = newsl + (s2-s1) * pic->sh; newtl = pic->tl + t1 * pic->th; newth = newtl + (t2-t1) * pic->th; R2D_Image(x, y, w, h, newsl, newtl, newsh, newth, pic->atlas); } void R2D_ImageFlush(void) { BE_DrawMesh_Single(draw_active_shader, &draw_mesh, NULL, draw_active_flags); R2D_Flush = NULL; draw_active_shader = NULL; } //awkward and weird to use void R2D_Image(float x, float y, float w, float h, float s1, float t1, float s2, float t2, mpic_t *pic) { int i; if (!pic) return; //don't draw pics if they have an image which is still loading. for (i = 0; i < pic->numpasses; i++) { if (pic->passes[i].texgen == T_GEN_SINGLEMAP && pic->passes[i].anim_frames[0] && pic->passes[i].anim_frames[0]->status == TEX_LOADING) return; if (pic->passes[i].texgen == T_GEN_DIFFUSE && pic->defaulttextures->base && pic->defaulttextures->base->status == TEX_LOADING) return; } if (draw_active_shader != pic || draw_active_flags != r2d_be_flags || R2D_Flush != R2D_ImageFlush || draw_mesh.numvertexes+4 > DRAW_QUADS) { if (R2D_Flush) R2D_Flush(); draw_active_shader = pic; draw_active_flags = r2d_be_flags; R2D_Flush = R2D_ImageFlush; draw_mesh.numindexes = 0; draw_mesh.numvertexes = 0; } Vector2Set(draw_mesh_xyz[draw_mesh.numvertexes+0], x, y); Vector2Set(draw_mesh_st[draw_mesh.numvertexes+0], s1, t1); Vector4Copy(draw_active_colour, draw_mesh_colors[draw_mesh.numvertexes+0]); Vector2Set(draw_mesh_xyz[draw_mesh.numvertexes+1], x+w, y); Vector2Set(draw_mesh_st[draw_mesh.numvertexes+1], s2, t1); Vector4Copy(draw_active_colour, draw_mesh_colors[draw_mesh.numvertexes+1]); Vector2Set(draw_mesh_xyz[draw_mesh.numvertexes+2], x+w, y+h); Vector2Set(draw_mesh_st[draw_mesh.numvertexes+2], s2, t2); Vector4Copy(draw_active_colour, draw_mesh_colors[draw_mesh.numvertexes+2]); Vector2Set(draw_mesh_xyz[draw_mesh.numvertexes+3], x, y+h); Vector2Set(draw_mesh_st[draw_mesh.numvertexes+3], s1, t2); Vector4Copy(draw_active_colour, draw_mesh_colors[draw_mesh.numvertexes+3]); draw_mesh.numvertexes += 4; draw_mesh.numindexes += 6; } void R2D_Image2dQuad(vec2_t const*points, vec2_t const*texcoords, vec4_t const*rgba, mpic_t *pic) { int i; if (!pic) return; //don't draw pics if they have an image which is still loading. for (i = 0; i < pic->numpasses; i++) { if (pic->passes[i].texgen == T_GEN_SINGLEMAP && pic->passes[i].anim_frames[0] && pic->passes[i].anim_frames[0]->status == TEX_LOADING) return; if (pic->passes[i].texgen == T_GEN_DIFFUSE && pic->defaulttextures->base && pic->defaulttextures->base->status == TEX_LOADING) return; } if (draw_active_shader != pic || draw_active_flags != r2d_be_flags || R2D_Flush != R2D_ImageFlush || draw_mesh.numvertexes+4 > DRAW_QUADS) { if (R2D_Flush) R2D_Flush(); draw_active_shader = pic; draw_active_flags = r2d_be_flags; R2D_Flush = R2D_ImageFlush; draw_mesh.numindexes = 0; draw_mesh.numvertexes = 0; } for (i = 0; i < 4; i++) { Vector2Copy(points[i], draw_mesh_xyz[draw_mesh.numvertexes+i]); Vector2Copy(texcoords[i], draw_mesh_st[draw_mesh.numvertexes+i]); if (rgba) Vector4Copy(rgba[i], draw_mesh_colors[draw_mesh.numvertexes+i]); else Vector4Copy(draw_active_colour, draw_mesh_colors[draw_mesh.numvertexes+i]); } draw_mesh.numvertexes += 4; draw_mesh.numindexes += 6; } /*draws a block of the current colour on the screen*/ void R2D_FillBlock(float x, float y, float w, float h) { mpic_t *pic; if (draw_active_colour[3] != 1) pic = shader_draw_fill_trans; else pic = shader_draw_fill; if (draw_active_shader != pic || draw_active_flags != r2d_be_flags || R2D_Flush != R2D_ImageFlush || draw_mesh.numvertexes+4 > DRAW_QUADS) { if (R2D_Flush) R2D_Flush(); draw_active_shader = pic; draw_active_flags = r2d_be_flags; R2D_Flush = R2D_ImageFlush; draw_mesh.numindexes = 0; draw_mesh.numvertexes = 0; } Vector2Set(draw_mesh_xyz[draw_mesh.numvertexes+0], x, y); Vector4Copy(draw_active_colour, draw_mesh_colors[draw_mesh.numvertexes+0]); Vector2Set(draw_mesh_xyz[draw_mesh.numvertexes+1], x+w, y); Vector4Copy(draw_active_colour, draw_mesh_colors[draw_mesh.numvertexes+1]); Vector2Set(draw_mesh_xyz[draw_mesh.numvertexes+2], x+w, y+h); Vector4Copy(draw_active_colour, draw_mesh_colors[draw_mesh.numvertexes+2]); Vector2Set(draw_mesh_xyz[draw_mesh.numvertexes+3], x, y+h); Vector4Copy(draw_active_colour, draw_mesh_colors[draw_mesh.numvertexes+3]); draw_mesh.numvertexes += 4; draw_mesh.numindexes += 6; } void R2D_Line(float x1, float y1, float x2, float y2, shader_t *shader) { if (R2D_Flush) { R2D_Flush(); R2D_Flush = NULL; } VectorSet(draw_mesh_xyz[0], x1, y1, 0); Vector2Set(draw_mesh_st[0], 0, 0); Vector4Copy(draw_active_colour, draw_mesh_colors[0]); VectorSet(draw_mesh_xyz[1], x2, y2, 0); Vector2Set(draw_mesh_st[1], 1, 0); Vector4Copy(draw_active_colour, draw_mesh_colors[1]); if (!shader) { if (draw_active_colour[3] != 1) shader = shader_draw_fill_trans; else shader = shader_draw_fill; } draw_mesh.numvertexes = 2; draw_mesh.numindexes = 2; BE_DrawMesh_Single(shader, &draw_mesh, NULL, BEF_LINES); } void R2D_ScalePic (float x, float y, float width, float height, mpic_t *pic) { R2D_Image(x, y, width, height, 0, 0, 1, 1, pic); } void R2D_SubPic(float x, float y, float width, float height, mpic_t *pic, float srcx, float srcy, float srcwidth, float srcheight) { float newsl, newtl, newsh, newth; newsl = (srcx)/(float)srcwidth; newsh = newsl + (width)/(float)srcwidth; newtl = (srcy)/(float)srcheight; newth = newtl + (height)/(float)srcheight; R2D_Image(x, y, width, height, newsl, newtl, newsh, newth, pic); } void R2D_Letterbox(float sx, float sy, float sw, float sh, mpic_t *pic, float pw, float ph) { float ratiox = (float)pw / sw; float ratioy = (float)ph / sh; if (pw<=0 || ph<=0) { //no size info... R2D_ImageColours(0, 0, 0, 1); R2D_FillBlock(sx, sy, sw, sh); R2D_ScalePic(sx, sy, 0, 0, pic); //in case its a videoshader with audio } else if (ratiox > ratioy) { //x ratio is greatest float h = (sw * ph) / pw; float p = sh - h; //letterbox R2D_ImageColours(0, 0, 0, 1); R2D_FillBlock(sx, sy, sw, p/2); R2D_FillBlock(sx, sy + h + (p/2), sw, p/2); R2D_ImageColours(1, 1, 1, 1); R2D_ScalePic(sx, sy + p/2, sw, h, pic); } else { //y ratio is greatest float w = (sh * pw) / ph; float p = sw - w; //sidethingies R2D_ImageColours(0, 0, 0, 1); R2D_FillBlock(sx, sy, (p/2), sh); R2D_FillBlock(sx + w + (p/2), sy, p/2, sh); R2D_ImageColours(1, 1, 1, 1); R2D_ScalePic(sx + p/2, sy, w, sh, pic); } } /* this is an ugly special case drawing func that's only used for the player color selection menu */ void R2D_TransPicTranslate (float x, float y, int width, int height, qbyte *pic, unsigned int *palette) { int v, u; unsigned trans[64*64], *dest; qbyte *src; dest = trans; for (v=0 ; v<64 ; v++, dest += 64) { src = &pic[ ((v*height)>>6) *width]; for (u=0 ; u<64 ; u++) dest[u] = palette[src[(u*width)>>6]]; } if (!TEXVALID(translate_texture)) { translate_texture = Image_CreateTexture("***translatedpic***", NULL, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA); translate_shader = R_RegisterShader("translatedpic", SUF_2D, "{\n" "if $nofixed\n" "program default2d\n" "endif\n" "nomipmaps\n" "{\n" "map $diffuse\n" "blendfunc blend\n" "rgbgen vertex\n" "alphagen vertex\n" "}\n" "}\n"); translate_shader->defaulttextures->base = translate_texture; } /* could avoid reuploading already translated textures but this func really isn't used enough anyway */ Image_Upload(translate_texture, TF_RGBA32, trans, NULL, 64, 64, 1, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA); R2D_ScalePic(x, y, width, height, translate_shader); } /* ================ Draw_ConsoleBackground ================ */ void R2D_ConsoleBackground (int firstline, int lastline, qboolean forceopaque) { float a; int w, h; w = vid.width; h = vid.height; if (forceopaque) { a = 1; // console background is necessary } else { if (!scr_conalpha.value) return; a = scr_conalpha.value; } if (R_GetShaderSizes(conback, NULL, NULL, false) <= 0) { R2D_ImageColours(0, 0, 0, a); R2D_FillBlock(0, lastline-(int)vid.height, w, h); R2D_ImageColours(1, 1, 1, 1); } else if (a >= 1) { R2D_ImageColours(1, 1, 1, 1); R2D_ScalePic(0, lastline-(int)vid.height, w, h, conback); } else { R2D_ImageColours(1, 1, 1, a); R2D_ScalePic (0, lastline - (int)vid.height, w, h, conback); R2D_ImageColours(1, 1, 1, 1); } } void R2D_EditorBackground (void) { R2D_ImageColours(0, 0, 0, 1); R2D_FillBlock(0, 0, vid.width, vid.height); // R2D_ScalePic(0, 0, vid.width, vid.height, conback); } /* ============= Draw_TileClear This repeats a 64*64 tile graphic to fill the screen around a sized down refresh window. ============= */ void R2D_TileClear (float x, float y, float w, float h) { float newsl, newsh, newtl, newth; newsl = (x)/(float)64; newsh = newsl + (w)/(float)64; newtl = (y)/(float)64; newth = newtl + (h)/(float)64; R2D_ImageColours(1,1,1,1); R2D_Image(x, y, w, h, newsl, newtl, newsh, newth, draw_backtile); } void QDECL R2D_Conback_Callback(struct cvar_s *var, char *oldvalue) { if (qrenderer == QR_NONE || !strcmp(var->string, "none")) { conback = NULL; return; } if (*var->string) conback = R_RegisterPic(var->string, NULL); #ifdef HAVE_LEGACY else if (Cvar_FindVar("scr_conalphafactor")) { //dp bullshit conback = R_RegisterShader("gfx/conback", SUF_2D, "{\n" "nomipmaps\n" "{\n" "map gfx/conback\n" "rgbgen const $scr_conbrightness\n" "alphagen const $scr_conalphafactor\n" "tcmod scroll $scr_conscroll_x $scr_conscroll_y\n" "blendfunc blend\n" "}\n" "{\n" "map gfx/conback2\n" "rgbgen const $scr_conbrightness\n" "alphagen const $scr_conalpha2factor\n" "tcmod scroll $scr_conscroll2_x $scr_conscroll2_y\n" "blendfunc blend\n" "}\n" "{\n" "map gfx/conback3\n" "rgbgen const $scr_conbrightness\n" "alphagen const $scr_conalpha3factor\n" "tcmod scroll $scr_conscroll3_x $scr_conscroll3_y\n" "blendfunc blend\n" "}\n" "}\n"); } #endif if (!R_GetShaderSizes(conback, NULL, NULL, true)) { conback = R_RegisterCustom(NULL, "console", SUF_2D, NULL, NULL); //quake3 if (!R_GetShaderSizes(conback, NULL, NULL, true)) { #ifdef HEXEN2 if (M_GameType() == MGT_HEXEN2) conback = R_RegisterPic("gfx/menu/conback.lmp", NULL); else #endif if (M_GameType() == MGT_QUAKE2) conback = R_RegisterPic("pics/conback.pcx", NULL); else conback = R_RegisterPic("gfx/conback.lmp", NULL); } } } #ifdef AVAIL_FREETYPE #if defined(_WIN32) && !defined(FTE_SDL) && !defined(WINRT) && !defined(_XBOX) #include qboolean R2D_Font_WasAdded(char *buffer, char *fontfilename) { char *match; if (!fontfilename) return true; match = strstr(buffer, fontfilename); if (!match) return false; if (!(match == buffer || match[-1] == ',')) return false; match += strlen(fontfilename); if (*match && *match != ',') return false; return true; } extern qboolean WinNT; void R2D_Font_AddFontLink(char *buffer, int buffersize, char *fontname) { char link[1024]; char *res, *comma, *othercomma, *nl; if (fontname) if (MyRegGetStringValueMultiSz(HKEY_LOCAL_MACHINE, WinNT?"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink":"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\FontLink\\SystemLink", fontname, link, sizeof(link))) { res = nl = link; while (*nl) { nl += strlen(nl); nl++; comma = strchr(res, ','); if (comma) { *comma++ = 0; othercomma = strchr(comma, ','); if (othercomma) *othercomma = 0; } else comma = ""; if (!R2D_Font_WasAdded(buffer, res)) { Q_strncatz(buffer, ",", buffersize); Q_strncatz(buffer, res, buffersize); R2D_Font_AddFontLink(buffer, buffersize, comma); } res = nl; } } } #else int R2D_Font_ListSystemFonts(const char *fname, qofs_t fsize, time_t modtime, void *ctx, searchpathfuncs_t *spath) { char tmp[MAX_QPATH]; COM_StripExtension(fname, tmp, sizeof(tmp)); Con_Printf("^[/gl_font %s^]\n", tmp); return true; } #endif #endif void R2D_Font_Changed(void) { float tsize; const char *con_font_name = con_textfont.string; unsigned int flags; if (!con_textsize.modified) return; if (!*con_font_name) con_font_name = gl_font.string; con_textsize.modified = false; if (con_textsize.value < 0) tsize = (-con_textsize.value * vid.height) / vid.pixelheight; //size defined in physical pixels else tsize = con_textsize.value; //size defined in virtual pixels. if (!tsize) tsize = 8; if (font_console == font_default) font_console = NULL; if (font_console) Font_Free(font_console); font_console = NULL; if (font_default) Font_Free(font_default); font_default = NULL; if (font_tiny) Font_Free(font_tiny); font_tiny = NULL; #if defined(MENU_DAT) || defined(CSQC_DAT) PR_ReloadFonts(true); #endif if (qrenderer == QR_NONE) return; flags = 0; if (r_font_postprocess_mono.ival) flags |= FONT_MONO; if (!strcmp(gl_font.string, "?")) { #ifndef AVAIL_FREETYPE Cvar_Set(&gl_font, ""); #elif defined(_WIN32) && !defined(FTE_SDL) && !defined(WINRT) && !defined(_XBOX) BOOL (APIENTRY *pChooseFontW)(LPCHOOSEFONTW) = NULL; dllfunction_t funcs[] = { {(void*)&pChooseFontW, "ChooseFontW"}, {NULL} }; LOGFONTW lf = {0}; CHOOSEFONTW cf = {sizeof(cf)}; extern HWND mainwindow; font_default = Font_LoadFont("", 8, 1, r_font_postprocess_outline.ival, flags); if (tsize != 8) font_console = Font_LoadFont("", tsize, 1, r_font_postprocess_outline.ival, flags); if (!font_console) font_console = font_default; cf.hwndOwner = mainwindow; cf.iPointSize = (8 * vid.rotpixelheight)/vid.height; cf.Flags = CF_FORCEFONTEXIST | CF_TTONLY; cf.lpLogFont = &lf; Sys_LoadLibrary("comdlg32.dll", funcs); if (pChooseFontW && pChooseFontW(&cf)) { char fname[MAX_OSPATH*8]; char lfFaceName[MAX_OSPATH]; char *keyname; narrowen(lfFaceName, sizeof(lfFaceName), lf.lfFaceName); *fname = 0; //FIXME: should enumerate and split & and ignore sizes and () crap. if (!*fname) { keyname = va("%s%s%s (TrueType)", lfFaceName, lf.lfWeight>=FW_BOLD?" Bold":"", lf.lfItalic?" Italic":""); if (!MyRegGetStringValue(HKEY_LOCAL_MACHINE, WinNT?"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts":"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts", keyname, fname, sizeof(fname))) *fname = 0; } if (!*fname) { keyname = va("%s (OpenType)", lfFaceName); if (!MyRegGetStringValue(HKEY_LOCAL_MACHINE, WinNT?"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts":"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts", keyname, fname, sizeof(fname))) *fname = 0; } R2D_Font_AddFontLink(fname, sizeof(fname), lfFaceName); Cvar_Set(&gl_font, fname); } return; #else Sys_EnumerateFiles("/usr/share/fonts/truetype/", "*/*.ttf", R2D_Font_ListSystemFonts, NULL, NULL); COM_EnumerateFiles("*.ttf", R2D_Font_ListSystemFonts, NULL); Cvar_Set(&gl_font, ""); #endif } if (COM_FDepthFile("fonts/qfont.kfont", true) <= COM_FDepthFile("gfx/mainmenu.lmp", true)) font_menu = Font_LoadFont("qfont", 20, 1, r_font_postprocess_outline.ival, flags); else font_menu = NULL; font_default = Font_LoadFont(gl_font.string, 8, 1, r_font_postprocess_outline.ival, flags); if (!font_default && *gl_font.string) font_default = Font_LoadFont("", 8, 1, r_font_postprocess_outline.ival, flags); if (tsize != 8 || strcmp(gl_font.string, con_font_name)) { font_console = Font_LoadFont(con_font_name, tsize, 1, r_font_postprocess_outline.ival, flags); if (!font_console) font_console = Font_LoadFont("", tsize, 1, r_font_postprocess_outline.ival, flags); } if (!font_console) font_console = font_default; //these are here instead of R2D_Console_Resize because this is guarenteed to happen in a sane place, while R2D_Console_Resize can happen during waits. #ifdef MENU_NATIVECODE if (mn_entry) mn_entry->Init(MI_RESOLUTION, vid.width, vid.height, vid.rotpixelwidth, vid.rotpixelheight); #endif #ifdef PLUGINS Plug_ResChanged(false); #endif } static void QDECL R2D_Font_Callback(struct cvar_s *var, char *oldvalue) { con_textsize.modified = true; } // console size manipulation callbacks void R2D_Console_Resize(void) { extern cvar_t gl_font; extern cvar_t vid_conwidth, vid_conheight; int cwidth, cheight; float xratio; float yratio=0; float ang, rad; extern cvar_t gl_screenangle; int fbwidth = vid.pixelwidth; int fbheight = vid.pixelheight; if (vid.framebuffer) { fbwidth = vid.framebuffer->width; fbheight = vid.framebuffer->height; } ang = (gl_screenangle.value>0?(gl_screenangle.value+45):(gl_screenangle.value-45))/90; ang = (int)ang * 90; if (ang) { rad = (ang * M_PI) / 180; vid.rotpixelwidth = fabs(cos(rad)) * (fbwidth) + fabs(sin(rad)) * (fbheight); vid.rotpixelheight = fabs(sin(rad)) * (fbwidth) + fabs(cos(rad)) * (fbheight); } else { vid.rotpixelwidth = fbwidth; vid.rotpixelheight = fbheight; } cwidth = vid_conwidth.value; cheight = vid_conheight.value; xratio = vid_conautoscale.value; if (xratio > 0) { char *s = strchr(vid_conautoscale.string, ' '); if (s) yratio = atof(s + 1); if (yratio <= 0) yratio = xratio; xratio = 1 / xratio; yratio = 1 / yratio; //autoscale overrides conwidth/height (without actually changing them) cwidth = vid.rotpixelwidth; cheight = vid.rotpixelheight; } else { xratio = 1; yratio = 1; } if (!cwidth && !cheight) { int i = 0, nh; int biggest = 0; //find the biggest artwork size that won't get minified for (i = 0; i < countof(vid_baseheight.vec4); i++) if (biggest < vid_baseheight.vec4[i] && vid_baseheight.vec4[i] < vid.rotpixelheight && vid_baseheight.vec4[i] >= vid_minsize.vec4[1]) biggest = vid_baseheight.vec4[i]; if (biggest) { nh = vid.rotpixelheight; while ((nh>>1) >= biggest) nh >>= 1; cheight = nh; } else { if (vid.dpi_y) cheight = (480 * 96) / vid.dpi_y; else cheight = 480; } } if (cheight && !cwidth && vid.rotpixelheight) cwidth = (cheight*vid.rotpixelwidth)/vid.rotpixelheight; if (cwidth && !cheight && vid.rotpixelwidth) cheight = (cwidth*vid.rotpixelheight)/vid.rotpixelwidth; if (!cwidth) cwidth = vid.rotpixelwidth; if (!cheight) cheight = vid.rotpixelheight; cwidth*=xratio; cheight*=yratio; //never go lower than the mod's minimum, UI elements would end up off-screen. if (cwidth < vid_minsize.vec4[0]) cwidth = vid_minsize.vec4[0]; if (cheight < vid_minsize.vec4[1]) cheight = vid_minsize.vec4[1]; //the engine has its own requirements too, sorry, though its unlikely for mods go lower. if (cwidth < 320) cwidth = 320; if (cheight < 200) cheight = 200; vid.width = cwidth; vid.height = cheight; Cvar_ForceCallback(&gl_font); } static void QDECL R2D_Conheight_Callback(struct cvar_s *var, char *oldvalue) { if (var->value > 1536) //anything higher is unreadable. { Cvar_ForceSet(var, "1536"); return; } if (var->value < 200 && var->value) //lower would be wrong { Cvar_ForceSet(var, "200"); return; } R2D_Console_Resize(); } static void QDECL R2D_Conwidth_Callback(struct cvar_s *var, char *oldvalue) { //let let the user be too crazy if (var->value > 2048) //anything higher is unreadable. { Cvar_ForceSet(var, "2048"); return; } if (var->value < 320 && var->value) //lower would be wrong { Cvar_ForceSet(var, "320"); return; } R2D_Console_Resize(); } static void QDECL R2D_Conautoscale_Callback(struct cvar_s *var, char *oldvalue) { R2D_Console_Resize(); } static void QDECL R2D_ScreenAngle_Callback(struct cvar_s *var, char *oldvalue) { R2D_Console_Resize(); } /* ============ R_PolyBlend ============ */ //bright flashes and stuff, game only, doesn't touch sbar void R2D_PolyBlend (void) { float bordersize = gl_cshiftborder.value; if (r_refdef.flags & RDF_NOWORLDMODEL) return; if (r_refdef.playerview->bordertint[3]) { vec2_t points[4]; vec2_t tcoords[4]; vec4_t colours[4]; int i; if (!bordersize) bordersize = 64; bordersize = min(bordersize, min(r_refdef.vrect.width, r_refdef.vrect.height)/2); for (i = 0; i < 4; i++) { Vector4Copy(r_refdef.playerview->bordertint, colours[0]); Vector4Copy(r_refdef.playerview->bordertint, colours[1]); Vector4Copy(r_refdef.playerview->bordertint, colours[2]); Vector4Copy(r_refdef.playerview->bordertint, colours[3]); switch(i) { case 0: //top Vector2Set(points[0], r_refdef.vrect.x, r_refdef.vrect.y); Vector2Set(points[1], r_refdef.vrect.x+r_refdef.vrect.width, r_refdef.vrect.y); Vector2Set(points[2], r_refdef.vrect.x+r_refdef.vrect.width-bordersize, r_refdef.vrect.y+bordersize); Vector2Set(points[3], r_refdef.vrect.x+bordersize, r_refdef.vrect.y+bordersize); colours[2][3] = colours[3][3] = 0; break; case 1: //bottom Vector2Set(points[0], r_refdef.vrect.x+bordersize, r_refdef.vrect.y+r_refdef.vrect.height-bordersize); Vector2Set(points[1], r_refdef.vrect.x+r_refdef.vrect.width-bordersize, r_refdef.vrect.y+r_refdef.vrect.height-bordersize); Vector2Set(points[2], r_refdef.vrect.x+r_refdef.vrect.width, r_refdef.vrect.y+r_refdef.vrect.height); Vector2Set(points[3], r_refdef.vrect.x, r_refdef.vrect.y+r_refdef.vrect.height); colours[0][3] = colours[1][3] = 0; break; case 2: //left Vector2Set(points[0], r_refdef.vrect.x, r_refdef.vrect.y); Vector2Set(points[1], r_refdef.vrect.x+bordersize, r_refdef.vrect.y+bordersize); Vector2Set(points[2], r_refdef.vrect.x+bordersize, r_refdef.vrect.y+r_refdef.vrect.height-bordersize); Vector2Set(points[3], r_refdef.vrect.x, r_refdef.vrect.y+r_refdef.vrect.height); colours[1][3] = colours[2][3] = 0; break; case 3: //right Vector2Set(points[0], r_refdef.vrect.x+r_refdef.vrect.width-bordersize, r_refdef.vrect.y+bordersize); Vector2Set(points[1], r_refdef.vrect.x+r_refdef.vrect.width, r_refdef.vrect.y); Vector2Set(points[2], r_refdef.vrect.x+r_refdef.vrect.width, r_refdef.vrect.y+r_refdef.vrect.height); Vector2Set(points[3], r_refdef.vrect.x+r_refdef.vrect.width-bordersize, r_refdef.vrect.y+r_refdef.vrect.height-bordersize); colours[0][3] = colours[3][3] = 0; break; } Vector2Set(tcoords[0], points[0][0]/64, points[0][1]/64); Vector2Set(tcoords[1], points[1][0]/64, points[1][1]/64); Vector2Set(tcoords[2], points[2][0]/64, points[2][1]/64); Vector2Set(tcoords[3], points[3][0]/64, points[3][1]/64); R2D_Image2dQuad((const vec2_t*)points, (const vec2_t*)tcoords, (const vec4_t*)colours, shader_polyblend); } } if (r_refdef.playerview->screentint[3]) { R2D_ImageColours (SRGBA(r_refdef.playerview->screentint[0], r_refdef.playerview->screentint[1], r_refdef.playerview->screentint[2], r_refdef.playerview->screentint[3])); R2D_ScalePic(r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height, shader_polyblend); R2D_ImageColours (1, 1, 1, 1); } } //for lack of hardware gamma void R2D_BrightenScreen (void) { float f; RSpeedMark(); if (fabs(v_contrast.value - 1.0) < 0.05 && fabs(v_contrastboost.value - 1.0) < 0.05 && fabs(v_brightness.value - 0) < 0.05 && fabs(v_gamma.value - 1) < 0.05) return; //don't go crazy with brightness. that makes it unusable and is thus unsafe - and worse, lots of people assume its based around 1 (like gamma and contrast are). cap to 0.5 if (v_brightness.value > 0.5) v_brightness.value = 0.5; if (v_contrast.value < 0.5) v_contrast.value = 0.5; if (r2d_canhwgamma || vid_hardwaregamma.ival == 4) return; TRACE(("R2D_BrightenScreen: brightening\n")); if ((v_gamma.value != 1 || v_contrast.value > 3 || v_contrastboost.value != 1) && shader_gammacb->prog) { //this should really be done properly, with render-to-texture R2D_ImageColours (v_gammainverted.ival?v_gamma.value:(1/v_gamma.value), v_contrast.value, v_brightness.value, v_contrastboost.value); R2D_Image(0, 0, vid.width, vid.height, 0, 0, 1, 1, shader_gammacb); } else { f = v_contrast.value; f = min (f, 3); while (f > 1) { if (f >= 2) { R2D_ImageColours (1, 1, 1, 1); f *= 0.5; } else { R2D_ImageColours (f - 1, f - 1, f - 1, 1); f = 1; } R2D_ScalePic(0, 0, vid.width, vid.height, shader_contrastup); } if (f < 1) { R2D_ImageColours (f, f, f, 1); R2D_ScalePic(0, 0, vid.width, vid.height, shader_contrastdown); } if (v_brightness.value) { R2D_ImageColours (v_brightness.value, v_brightness.value, v_brightness.value, 1); R2D_ScalePic(0, 0, vid.width, vid.height, shader_brightness); } } R2D_ImageColours (1, 1, 1, 1); /*make sure the hud is redrawn after if needed*/ Sbar_Changed(); RSpeedEnd(RSPEED_PALETTEFLASHES); } //for menus void R2D_FadeScreen (void) { R2D_ScalePic(0, 0, vid.width, vid.height, shader_menutint); Sbar_Changed(); } qboolean R2D_DrawLevelshot(void) { extern char levelshotname[]; extern cvar_t scr_loadingscreen_aspect; if (*levelshotname) { shader_t *pic = R2D_SafeCachePic (levelshotname); int w,h; if (!R_GetShaderSizes(pic, &w, &h, true)) { #ifdef Q3CLIENT pic = R2D_SafeCachePic("menu/art/unkownmap"); if (!R_GetShaderSizes(pic, &w, &h, true)) #endif w = h = 1; } switch(scr_loadingscreen_aspect.ival) { case 0: //use the source image's aspect break; case 1: //q3 assumed 4:3 resolutions, with power-of-two images. lame, but lets retain the aspect w = 640; h = 480; break; case 2: //just ignore aspect entirely and stretch the thing in hideous ways w = vid.width; h = vid.height; break; } R2D_Letterbox(0, 0, vid.width, vid.height, pic, w, h); //q3's noise. pic = R2D_SafeCachePic("levelShotDetail"); if (R_GetShaderSizes(pic, &w, &h, true)) R2D_Image(0, 0, vid.width, vid.height, 0, 0, vid.width/256, vid.height/256, pic); return true; } return false; } //crosshairs #define CS_HEIGHT 8 #define CS_WIDTH 8 unsigned char crosshair_pixels[] = { // 2 + (spaced) 0x08, 0x00, 0x08, 0x55, 0x08, 0x00, 0x08, 0x00, // 3 + 0x00, 0x08, 0x08, 0x36, 0x08, 0x08, 0x00, 0x00, // 4 X 0x00, 0x22, 0x14, 0x00, 0x14, 0x22, 0x00, 0x00, // 5 X (spaced) 0x41, 0x00, 0x14, 0x00, 0x14, 0x00, 0x41, 0x00, // 6 diamond (unconnected) 0x00, 0x14, 0x22, 0x00, 0x22, 0x14, 0x00, 0x00, // 7 diamond 0x00, 0x08, 0x14, 0x22, 0x14, 0x08, 0x00, 0x00, // 8 four points 0x00, 0x08, 0x00, 0x22, 0x00, 0x08, 0x00, 0x00, // 9 three points 0x00, 0x00, 0x08, 0x00, 0x22, 0x00, 0x00, 0x00, // 10 0x08, 0x2a, 0x00, 0x63, 0x00, 0x2a, 0x08, 0x00, // 11 0x49, 0x2a, 0x00, 0x63, 0x00, 0x2a, 0x49, 0x00, // 12 horizontal line 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, // 13 vertical line 0x08, 0x08, 0x08, 0x00, 0x08, 0x08, 0x08, 0x00, // 14 larger + 0x08, 0x08, 0x08, 0x77, 0x08, 0x08, 0x08, 0x00, // 15 angle 0x00, 0x00, 0x00, 0x70, 0x08, 0x08, 0x08, 0x00, // 16 dot 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, // 17 weird angle thing 0x00, 0x00, 0x00, 0x38, 0x48, 0x08, 0x10, 0x00, // 18 circle w/ dot 0x00, 0x00, 0x00, 0x6b, 0x41, 0x63, 0x3e, 0x08, // 19 tripoint 0x08, 0x08, 0x08, 0x00, 0x14, 0x22, 0x41, 0x00, }; void R2D_Crosshair_Update(void) { int crossdata[CS_WIDTH*CS_HEIGHT]; int c; int w, h; unsigned char *x; c = crosshair.ival; if (!crosshairimage.string) return; else if (crosshairimage.string[0] && c == 1) { shader_crosshair->defaulttextures->base = R_LoadHiResTexture (crosshairimage.string, "crosshairs", IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA); if (TEXVALID(shader_crosshair->defaulttextures->base)) return; } else if (c <= 1) return; c -= 2; c = c % (sizeof(crosshair_pixels) / (CS_HEIGHT*sizeof(*crosshair_pixels))); if (!TEXVALID(ch_int_texture)) ch_int_texture = Image_CreateTexture("***crosshair***", NULL, IF_UIPIC|IF_NOMIPMAP); shader_crosshair->defaulttextures->base = ch_int_texture; Q_memset(crossdata, 0, sizeof(crossdata)); x = crosshair_pixels + (CS_HEIGHT * c); for (h = 0; h < CS_HEIGHT; h++) { int pix = x[h]; for (w = 0; w < CS_WIDTH; w++) { if (pix & 0x1) crossdata[CS_WIDTH * h + w] = 0xffffffff; pix >>= 1; } } Image_Upload(ch_int_texture, TF_RGBA32, crossdata, NULL, CS_WIDTH, CS_HEIGHT, 1, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA); } static void QDECL R2D_CrosshairImage_Callback(struct cvar_s *var, char *oldvalue) { R2D_Crosshair_Update(); } static void QDECL R2D_Crosshair_Callback(struct cvar_s *var, char *oldvalue) { R2D_Crosshair_Update(); } static void QDECL R2D_CrosshairColor_Callback(struct cvar_s *var, char *oldvalue) { SCR_StringToRGB(var->string, ch_color, 255); ch_color[0] = bound(0, ch_color[0], 1); ch_color[1] = bound(0, ch_color[1], 1); ch_color[2] = bound(0, ch_color[2], 1); } void R2D_DrawCrosshair(void) { int x, y; int sc; float sx, sy, sizex, sizey; float size; if (crosshair.ival < 1) return; // old style if (crosshair.ival == 1 && !crosshairimage.string[0]) { // adjust console crosshair scale to match default size = crosshairsize.value; if (size == 0) size = 8; else if (size < 0) size = -size; for (sc = 0; sc < cl.splitclients; sc++) { SCR_CrosshairPosition(&cl.playerview[sc], &sx, &sy); Font_BeginScaledString(font_default, sx, sy, size, size, &sx, &sy); sx -= Font_CharScaleWidth(CON_WHITEMASK, '+' | 0xe000)/2; sy -= Font_CharScaleHeight()/2; R2D_ImageColours(ch_color[0], ch_color[1], ch_color[2], crosshairalpha.value); Font_DrawScaleChar(sx, sy, CON_WHITEMASK, '+' | 0xe000); Font_EndString(font_default); } R2D_ImageColours(1,1,1,1); return; } size = crosshairsize.value; if (size < 0) { size = -size; sizex = size; sizey = size; } else { sizex = (size*vid.rotpixelwidth) / (float)vid.width; sizey = (size*vid.rotpixelheight) / (float)vid.height; } sizex = (int)sizex; sizex = ((sizex)*(int)vid.width) / (float)vid.rotpixelwidth; sizey = (int)sizey; sizey = ((sizey)*(int)vid.height) / (float)vid.rotpixelheight; R2D_ImageColours(ch_color[0], ch_color[1], ch_color[2], crosshairalpha.value); for (sc = 0; sc < cl.splitclients; sc++) { SCR_CrosshairPosition(&cl.playerview[sc], &sx, &sy); //translate to pixel coord, for rounding x = ((sx-sizex+(sizex/CS_WIDTH))*vid.rotpixelwidth) / (float)vid.width; y = ((sy-sizey+(sizey/CS_HEIGHT))*vid.rotpixelheight) / (float)vid.height; //translate to screen coords sx = ((x)*(int)vid.width) / (float)vid.rotpixelwidth; sy = ((y)*(int)vid.height) / (float)vid.rotpixelheight; R2D_Image(sx, sy, sizex*2, sizey*2, 0, 0, 1, 1, shader_crosshair); } R2D_ImageColours(1, 1, 1, 1); } static texid_t internalrt; //resize a texture for a render target and specify the format of it. //pass TF_INVALID and sizes=0 to get without configuring (shaders that hardcode an $rt1 etc). texid_t R2D_RT_Configure(const char *id, int width, int height, uploadfmt_t rtfmt, unsigned int imageflags) { texid_t tid; if (!strcmp(id, "-")) { internalrt = tid = Image_CreateTexture("", NULL, imageflags); } else { tid = Image_FindTexture(id, NULL, imageflags); if (!TEXVALID(tid)) tid = Image_CreateTexture(id, NULL, imageflags); } if (rtfmt) { if (tid->flags != ((tid->flags & ~(IF_NEAREST|IF_LINEAR)) | (imageflags & (IF_NEAREST|IF_LINEAR)))) { tid->flags = ((tid->flags & ~(IF_NEAREST|IF_LINEAR)) | (imageflags & (IF_NEAREST|IF_LINEAR))); tid->width = -1; } Image_Upload(tid, rtfmt, NULL, NULL, width, height, 1, imageflags); tid->width = width; tid->height = height; } return tid; } texid_t R2D_RT_GetTexture(const char *id, unsigned int *width, unsigned int *height) { texid_t tid; if (!strcmp(id, "-")) { tid = internalrt; // internalrt = r_nulltex; } else tid = Image_FindTexture(id, NULL, RT_IMAGEFLAGS); if (tid) { *width = tid->width; *height = tid->height; } else { *width = 0; *height = 0; } return tid; } #endif