diff --git a/engine/client/cl_menu.c b/engine/client/cl_menu.c index ba9da8e8..c3170403 100644 --- a/engine/client/cl_menu.c +++ b/engine/client/cl_menu.c @@ -358,8 +358,8 @@ static HIMAGE pfnPIC_Load( const char *szPicName, const byte *image_buf, long im // add default parms to image flags |= TF_IMAGE; - host.decal_loading = true; - tx = GL_LoadTexture( szPicName, image_buf, image_size, flags ); + host.decal_loading = true; // allow decal images for menu + tx = GL_LoadTexture( szPicName, image_buf, image_size, flags, NULL ); host.decal_loading = false; return tx; diff --git a/engine/client/cl_remap.c b/engine/client/cl_remap.c index ed99bc4a..e86a61c5 100644 --- a/engine/client/cl_remap.c +++ b/engine/client/cl_remap.c @@ -126,7 +126,7 @@ void CL_DuplicateTexture( mstudiotexture_t *ptexture, int topcolor, int bottomco Q_memcpy( paletteBackup, pal, 768 ); raw = CL_CreateRawTextureFromPixels( tx, &size, topcolor, bottomcolor ); - ptexture->index = GL_LoadTexture( texname, raw, size, TF_FORCE_COLOR ); // do copy + ptexture->index = GL_LoadTexture( texname, raw, size, TF_FORCE_COLOR, NULL ); // do copy GL_SetTextureType( ptexture->index, TEX_REMAP ); // restore original palette diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index 3cc69e1a..b66c4827 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -259,7 +259,7 @@ void SCR_DrawPlaque( void ) if(( cl_allow_levelshots->integer && !cls.changelevel ) || Cvar_VariableInteger( "sv_background" )) { - levelshot = GL_LoadTexture( cl_levelshot_name->string, NULL, 0, TF_IMAGE ); + levelshot = GL_LoadTexture( cl_levelshot_name->string, NULL, 0, TF_IMAGE, NULL ); GL_SetRenderMode( kRenderNormal ); R_DrawStretchPic( 0, 0, scr_width->integer, scr_height->integer, 0, 0, 1, 1, levelshot ); @@ -445,7 +445,7 @@ static void SCR_LoadCreditsFont( void ) if( cls.creditsFont.valid ) return; // already loaded - cls.creditsFont.hFontTexture = GL_LoadTexture( "gfx.wad/creditsfont.fnt", NULL, 0, TF_IMAGE ); + cls.creditsFont.hFontTexture = GL_LoadTexture( "gfx.wad/creditsfont.fnt", NULL, 0, TF_IMAGE, NULL ); R_GetTextureParms( &fontWidth, NULL, cls.creditsFont.hFontTexture ); // setup creditsfont @@ -516,15 +516,15 @@ static void SCR_InstallParticlePalette( void ) void SCR_RegisterShaders( void ) { - cls.fillImage = GL_LoadTexture( "*white", NULL, 0, TF_IMAGE ); // used for FillRGBA - cls.particleImage = GL_LoadTexture( "*particle", NULL, 0, TF_IMAGE ); + cls.fillImage = GL_LoadTexture( "*white", NULL, 0, TF_IMAGE, NULL ); // used for FillRGBA + cls.particleImage = GL_LoadTexture( "*particle", NULL, 0, TF_IMAGE, NULL ); // register gfx.wad images - cls.pauseIcon = GL_LoadTexture( "gfx.wad/paused.lmp", NULL, 0, TF_IMAGE ); + cls.pauseIcon = GL_LoadTexture( "gfx.wad/paused.lmp", NULL, 0, TF_IMAGE, NULL ); if( cl_allow_levelshots->integer ) - cls.loadingBar = GL_LoadTexture( "gfx.wad/lambda.lmp", NULL, 0, TF_IMAGE|TF_LUMINANCE ); - else cls.loadingBar = GL_LoadTexture( "gfx.wad/lambda.lmp", NULL, 0, TF_IMAGE ); - cls.tileImage = GL_LoadTexture( "gfx.wad/backtile.lmp", NULL, 0, TF_UNCOMPRESSED|TF_NOPICMIP|TF_NOMIPMAP ); + cls.loadingBar = GL_LoadTexture( "gfx.wad/lambda.lmp", NULL, 0, TF_IMAGE|TF_LUMINANCE, NULL ); + else cls.loadingBar = GL_LoadTexture( "gfx.wad/lambda.lmp", NULL, 0, TF_IMAGE, NULL ); + cls.tileImage = GL_LoadTexture( "gfx.wad/backtile.lmp", NULL, 0, TF_UNCOMPRESSED|TF_NOPICMIP|TF_NOMIPMAP, NULL ); cls.hChromeSprite = pfnSPR_Load( "sprites/shellchrome.spr" ); } diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 744e0a56..d5fcfb7c 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -2649,7 +2649,7 @@ int CL_DecalIndex( int id ) Q_snprintf( decalname, sizeof( decalname ), "materials/decals/%s.tga", host.draw_decals[id] ); if( FS_FileExists( decalname, false )) - gl_texturenum = GL_LoadTexture( decalname, NULL, 0, TF_DECAL ); + gl_texturenum = GL_LoadTexture( decalname, NULL, 0, TF_DECAL, NULL ); if( gl_texturenum ) { @@ -2670,7 +2670,7 @@ int CL_DecalIndex( int id ) } } - if( !load_external ) cl.decal_index[id] = GL_LoadTexture( host.draw_decals[id], NULL, 0, TF_DECAL ); + if( !load_external ) cl.decal_index[id] = GL_LoadTexture( host.draw_decals[id], NULL, 0, TF_DECAL, NULL ); } host.decal_loading = false; diff --git a/engine/client/gl_backend.c b/engine/client/gl_backend.c index d4114bd7..077641ed 100644 --- a/engine/client/gl_backend.c +++ b/engine/client/gl_backend.c @@ -464,7 +464,7 @@ qboolean VID_ScreenShot( const char *filename, int shot_type ) break; } - Image_Process( &r_shot, width, height, 0.0f, flags ); + Image_Process( &r_shot, width, height, 0.0f, flags, NULL ); // write image result = FS_SaveImage( filename, r_shot ); @@ -530,7 +530,7 @@ qboolean VID_CubemapShot( const char *base, uint size, const float *vieworg, qbo r_side->size = r_side->width * r_side->height * 3; r_side->buffer = temp; - if( flags ) Image_Process( &r_side, 0, 0, 0.0f, flags ); + if( flags ) Image_Process( &r_side, 0, 0, 0.0f, flags, NULL ); Q_memcpy( buffer + (size * size * 3 * i), r_side->buffer, size * size * 3 ); } diff --git a/engine/client/gl_image.c b/engine/client/gl_image.c index 5b2ebee0..d97fce20 100644 --- a/engine/client/gl_image.c +++ b/engine/client/gl_image.c @@ -879,7 +879,7 @@ GL_UploadTexture upload texture into video memory =============== */ -static void GL_UploadTexture( rgbdata_t *pic, gltexture_t *tex, qboolean subImage ) +static void GL_UploadTexture( rgbdata_t *pic, gltexture_t *tex, qboolean subImage, imgfilter_t *filter ) { byte *buf, *data; const byte *bufend; @@ -929,7 +929,7 @@ static void GL_UploadTexture( rgbdata_t *pic, gltexture_t *tex, qboolean subImag img_flags |= IMAGE_FORCE_RGBA; // processing image before uploading (force to rgba, make luma etc) - if( pic->buffer ) Image_Process( &pic, 0, 0, 0.0f, img_flags ); + if( pic->buffer ) Image_Process( &pic, 0, 0, 0.0f, img_flags, filter ); if( tex->flags & TF_LUMINANCE ) { @@ -1069,7 +1069,7 @@ static void GL_UploadTexture( rgbdata_t *pic, gltexture_t *tex, qboolean subImag GL_LoadTexture ================ */ -int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags ) +int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags, imgfilter_t *filter ) { gltexture_t *tex; rgbdata_t *pic; @@ -1085,7 +1085,7 @@ int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags ) return 0; } - // get rid of black vertical line on a 'BlackMesa map' + // HACKHACK: get rid of black vertical line on a 'BlackMesa map' if( !Q_strcmp( name, "#lab1_map1.mip" ) || !Q_strcmp( name, "#lab1_map2.mip" )) { flags |= TF_NEAREST; @@ -1138,10 +1138,10 @@ int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags ) tex->texnum = tr.skyboxbasenum++; else tex->texnum = i; // texnum is used for fast acess into r_textures array too - GL_UploadTexture( pic, tex, false ); + GL_UploadTexture( pic, tex, false, filter ); GL_TexFilter( tex, false ); // update texture filter, wrap etc - if(!( flags & (TF_KEEP_8BIT|TF_KEEP_RGBDATA))) + if(!( flags & ( TF_KEEP_8BIT|TF_KEEP_RGBDATA ))) FS_FreeImage( pic ); // release source texture // add to hash table @@ -1221,7 +1221,7 @@ int GL_LoadTextureInternal( const char *name, rgbdata_t *pic, texFlags_t flags, tex->flags |= flags; } - GL_UploadTexture( pic, tex, update ); + GL_UploadTexture( pic, tex, update, NULL ); GL_TexFilter( tex, update ); // update texture filter, wrap etc if( !update ) @@ -1319,9 +1319,9 @@ void GL_ProcessTexture( int texnum, float gamma, int topColor, int bottomColor ) // all the operations makes over the image copy not an original pic = FS_CopyImage( image->original ); - Image_Process( &pic, topColor, bottomColor, gamma, flags ); + Image_Process( &pic, topColor, bottomColor, gamma, flags, NULL ); - GL_UploadTexture( pic, image, true ); + GL_UploadTexture( pic, image, true, NULL ); GL_TexFilter( image, true ); // update texture filter, wrap etc FS_FreeImage( pic ); @@ -1992,6 +1992,8 @@ void R_InitImages( void ) // set texture parameters R_SetTextureParameters(); R_InitBuiltinTextures(); + + R_ParseTexFilters( "scripts/texfilter.txt" ); } /* diff --git a/engine/client/gl_local.h b/engine/client/gl_local.h index 4ded1747..11400640 100644 --- a/engine/client/gl_local.h +++ b/engine/client/gl_local.h @@ -165,6 +165,7 @@ typedef struct int lightstylevalue[MAX_LIGHTSTYLES]; // value 0 - 65536 float lightcache[MAX_LIGHTSTYLES]; + float viewplanedist; mplane_t clipPlane; } ref_instance_t; @@ -307,7 +308,7 @@ void R_UploadStretchRaw( int texture, int cols, int rows, int width, int height, void R_SetTextureParameters( void ); gltexture_t *R_GetTexture( GLenum texnum ); void GL_SetTextureType( GLenum texnum, GLenum type ); -int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags ); +int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags, imgfilter_t *filter ); int GL_LoadTextureInternal( const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update ); byte *GL_ResampleTexture( const byte *source, int in_w, int in_h, int out_w, int out_h, qboolean isNormalMap ); int GL_CreateTexture( const char *name, int width, int height, const void *buffer, texFlags_t flags ); @@ -381,6 +382,11 @@ void Matrix4x4_CreateProjection(matrix4x4 out, float xMax, float xMin, float yMa void Matrix4x4_CreateOrtho(matrix4x4 m, float xLeft, float xRight, float yBottom, float yTop, float zNear, float zFar); void Matrix4x4_CreateModelview( matrix4x4 out ); +// +// gl_rmisc. +// +void R_ParseTexFilters( const char *filename ); +imgfilter_t *R_FindTexFilter( const char *texname ); // // gl_rsurf.c @@ -443,7 +449,7 @@ void R_Shutdown( void ); qboolean R_Init( void ); void R_Shutdown( void ); void VID_CheckChanges( void ); -int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags ); +int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags, imgfilter_t *filter ); void GL_FreeImage( const char *name ); qboolean VID_ScreenShot( const char *filename, int shot_type ); qboolean VID_CubemapShot( const char *base, uint size, const float *vieworg, qboolean skyshot ); diff --git a/engine/client/gl_rmain.c b/engine/client/gl_rmain.c index cbb86534..bf5bff2f 100644 --- a/engine/client/gl_rmain.c +++ b/engine/client/gl_rmain.c @@ -780,6 +780,9 @@ static void R_SetupFrame( void ) VectorCopy( RI.refdef.vieworg, RI.vieworg ); AngleVectors( RI.refdef.viewangles, RI.vforward, RI.vright, RI.vup ); + // setup viewplane dist + RI.viewplanedist = DotProduct( RI.refdef.vieworg, RI.vforward ); + if( !r_lockcull->integer ) { VectorCopy( RI.vforward, RI.cull_vforward ); @@ -1523,6 +1526,11 @@ static const char *GL_TextureName( unsigned int texnum ) return R_GetTexture( texnum )->name; } +static int GL_LoadTextureNoFilter( const char *name, const byte *buf, size_t size, int flags ) +{ + return GL_LoadTexture( name, buf, size, flags, NULL ); +} + static render_api_t gRenderAPI = { GL_RenderGetParm, @@ -1539,7 +1547,7 @@ static render_api_t gRenderAPI = R_StoreEfrags, GL_FindTexture, GL_TextureName, - GL_LoadTexture, + GL_LoadTextureNoFilter, GL_CreateTexture, GL_FreeTexture, DrawSingleDecal, diff --git a/engine/client/gl_rmisc.c b/engine/client/gl_rmisc.c index 65ba8fe2..c2541501 100644 --- a/engine/client/gl_rmisc.c +++ b/engine/client/gl_rmisc.c @@ -27,6 +27,15 @@ typedef struct int lMax; } dmaterial_t; +typedef struct +{ + char texname[64]; // shortname + imgfilter_t filter; +} dfilter_t; + +dfilter_t *tex_filters[MAX_TEXTURES]; +int num_texfilters; + // default rules for apply detail textures. // maybe move this to external script? static const dmaterial_t detail_table[] = @@ -246,7 +255,7 @@ void R_ParseDetailTextures( const char *filename ) if( Q_stricmp( tex->name, texname )) continue; - tex->dt_texturenum = GL_LoadTexture( detail_texname, NULL, 0, TF_FORCE_COLOR ); + tex->dt_texturenum = GL_LoadTexture( detail_texname, NULL, 0, TF_FORCE_COLOR, NULL ); // texture is loaded if( tex->dt_texturenum ) @@ -265,6 +274,117 @@ void R_ParseDetailTextures( const char *filename ) Mem_Free( afile ); } +void R_ParseTexFilters( const char *filename ) +{ + char *afile, *pfile; + string token, texname; + dfilter_t *tf; + int i; + + afile = FS_LoadFile( filename, NULL, false ); + if( !afile ) return; + + pfile = afile; + + // format: 'texturename' 'filtername' 'factor' 'bias' 'blendmode' 'grayscale' + while(( pfile = COM_ParseFile( pfile, token )) != NULL ) + { + qboolean parse_filter = false; + imgfilter_t filter; + + Q_memset( &filter, 0, sizeof( filter )); + Q_strncpy( texname, token, sizeof( texname )); + + // parse filter + pfile = COM_ParseFile( pfile, token ); + if( !Q_stricmp( token, "blur" )) + filter.filter = BLUR_FILTER; + else if( !Q_stricmp( token, "blur2" )) + filter.filter = BLUR_FILTER2; + else if( !Q_stricmp( token, "edge" )) + filter.filter = EDGE_FILTER; + else if( !Q_stricmp( token, "emboss" )) + filter.filter = EMBOSS_FILTER; + + // reading factor + pfile = COM_ParseFile( pfile, token ); + filter.factor = Q_atof( token ); + + // reading bias + pfile = COM_ParseFile( pfile, token ); + filter.bias = Q_atof( token ); + + // reading blendFunc + pfile = COM_ParseFile( pfile, token ); + if( !Q_stricmp( token, "modulate" ) || !Q_stricmp( token, "GL_MODULATE" )) + filter.blendFunc = GL_MODULATE; + else if( !Q_stricmp( token, "replace" ) || !Q_stricmp( token, "GL_REPLACE" )) + filter.blendFunc = GL_REPLACE; + else if( !Q_stricmp( token, "add" ) || !Q_stricmp( token, "GL_ADD" )) + filter.blendFunc = GL_ADD; + else if( !Q_stricmp( token, "decal" ) || !Q_stricmp( token, "GL_DECAL" )) + filter.blendFunc = GL_DECAL; + else if( !Q_stricmp( token, "blend" ) || !Q_stricmp( token, "GL_BLEND" )) + filter.blendFunc = GL_BLEND; + else if( !Q_stricmp( token, "add_signed" ) || !Q_stricmp( token, "GL_ADD_SIGNED" )) + filter.blendFunc = GL_ADD_SIGNED; + else MsgDev( D_WARN, "unknown blendFunc '%s' specified for texture '%s'\n", texname, token ); + + // reading flags + pfile = COM_ParseFile( pfile, token ); + filter.flags = Q_atoi( token ); + + // make sure what factor is not zeroed + if( filter.factor == 0.0f ) + { + MsgDev( D_WARN, "texfilter for texture %s has factor 0! Ignored\n", texname ); + continue; + } + + // check if already existed + for( i = 0; i < num_texfilters; i++ ) + { + tf = tex_filters[i]; + + if( !Q_stricmp( tf->texname, texname )) + { + MsgDev( D_WARN, "texture %s has specified multiple filters! Ignored\n", texname ); + break; + } + } + + if( i != num_texfilters ) + continue; // already specified + + // allocate new texfilter + tf = Z_Malloc( sizeof( dfilter_t )); + tex_filters[num_texfilters++] = tf; + + Q_strncpy( tf->texname, texname, sizeof( tf->texname )); + tf->filter = filter; + } + + MsgDev( D_INFO, "%i texture filters parsed\n", num_texfilters ); + + Mem_Free( afile ); +} + +imgfilter_t *R_FindTexFilter( const char *texname ) +{ + dfilter_t *tf; + int i; + + for( i = 0; i < num_texfilters; i++ ) + { + tf = tex_filters[i]; + + if( !Q_stricmp( tf->texname, texname )) + return &tf->filter; + } + + return NULL; +} + void R_NewMap( void ) { texture_t *tx; diff --git a/engine/client/gl_rsurf.c b/engine/client/gl_rsurf.c index d150821e..83594884 100644 --- a/engine/client/gl_rsurf.c +++ b/engine/client/gl_rsurf.c @@ -1304,7 +1304,7 @@ static int R_SurfaceCompare( const msurface_t **a, const msurface_t **b ) { msurface_t *surf1, *surf2; mextrasurf_t *info1, *info2; - vec3_t vecLength, org1, org2; + vec3_t org1, org2; float len1, len2; surf1 = (msurface_t *)*a; @@ -1316,10 +1316,9 @@ static int R_SurfaceCompare( const msurface_t **a, const msurface_t **b ) VectorAdd( RI.currententity->origin, info1->origin, org1 ); VectorAdd( RI.currententity->origin, info2->origin, org2 ); - VectorSubtract( RI.vieworg, org1, vecLength ); - len1 = VectorLength( vecLength ); - VectorSubtract( RI.vieworg, org2, vecLength ); - len2 = VectorLength( vecLength ); + // compare by plane dists + len1 = DotProduct( org1, RI.vforward ) - RI.viewplanedist; + len2 = DotProduct( org2, RI.vforward ) - RI.viewplanedist; if( len1 > len2 ) return -1; diff --git a/engine/client/gl_sprite.c b/engine/client/gl_sprite.c index d5b9c22a..605df0ff 100644 --- a/engine/client/gl_sprite.c +++ b/engine/client/gl_sprite.c @@ -66,7 +66,7 @@ static dframetype_t *R_SpriteLoadFrame( model_t *mod, void *pin, mspriteframe_t if( mod->flags & 256 ) // it's a HUD sprite { Q_snprintf( texname, sizeof( texname ), "#HUD/%s_%s_%i%i.spr", mod->name, group_suffix, num / 10, num % 10 ); - gl_texturenum = GL_LoadTexture( texname, pin, pinframe->width * pinframe->height, r_texFlags ); + gl_texturenum = GL_LoadTexture( texname, pin, pinframe->width * pinframe->height, r_texFlags, NULL ); } else { @@ -79,7 +79,7 @@ static dframetype_t *R_SpriteLoadFrame( model_t *mod, void *pin, mspriteframe_t Q_snprintf( texname, sizeof( texname ), "materials/%s/frame%i%i.tga", sprname, num / 10, num % 10 ); if( FS_FileExists( texname, false )) - gl_texturenum = GL_LoadTexture( texname, NULL, 0, r_texFlags ); + gl_texturenum = GL_LoadTexture( texname, NULL, 0, r_texFlags, NULL ); if( gl_texturenum ) load_external = true; // sucessfully loaded @@ -88,7 +88,7 @@ static dframetype_t *R_SpriteLoadFrame( model_t *mod, void *pin, mspriteframe_t if( !load_external ) { Q_snprintf( texname, sizeof( texname ), "#%s_%s_%i%i.spr", mod->name, group_suffix, num / 10, num % 10 ); - gl_texturenum = GL_LoadTexture( texname, pin, pinframe->width * pinframe->height, r_texFlags ); + gl_texturenum = GL_LoadTexture( texname, pin, pinframe->width * pinframe->height, r_texFlags, NULL ); } else MsgDev( D_NOTE, "loading HQ: %s\n", texname ); } @@ -319,7 +319,7 @@ void Mod_LoadMapSprite( model_t *mod, const void *buffer, size_t size, qboolean if( h < MAPSPRITE_SIZE ) h = MAPSPRITE_SIZE; // resample image if needed - Image_Process( &pix, w, h, 0.0f, IMAGE_FORCE_RGBA|IMAGE_RESAMPLE ); + Image_Process( &pix, w, h, 0.0f, IMAGE_FORCE_RGBA|IMAGE_RESAMPLE, NULL ); w = h = MAPSPRITE_SIZE; diff --git a/engine/client/gl_studio.c b/engine/client/gl_studio.c index c9243d8e..a45b1503 100644 --- a/engine/client/gl_studio.c +++ b/engine/client/gl_studio.c @@ -3373,7 +3373,7 @@ static void R_StudioLoadTexture( model_t *mod, studiohdr_t *phdr, mstudiotexture Q_snprintf( texname, sizeof( texname ), "materials/%s/%s.tga", mdlname, name ); if( FS_FileExists( texname, false )) - gl_texturenum = GL_LoadTexture( texname, NULL, 0, flags ); + gl_texturenum = GL_LoadTexture( texname, NULL, 0, flags, NULL ); if( gl_texturenum ) { @@ -3390,7 +3390,7 @@ static void R_StudioLoadTexture( model_t *mod, studiohdr_t *phdr, mstudiotexture // build the texname Q_snprintf( texname, sizeof( texname ), "#%s/%s.mdl", mdlname, name ); - ptexture->index = GL_LoadTexture( texname, (byte *)ptexture, size, flags ); + ptexture->index = GL_LoadTexture( texname, (byte *)ptexture, size, flags, NULL ); } else MsgDev( D_NOTE, "loading HQ: %s\n", texname ); diff --git a/engine/client/gl_warp.c b/engine/client/gl_warp.c index 6c524947..01df065b 100644 --- a/engine/client/gl_warp.c +++ b/engine/client/gl_warp.c @@ -444,7 +444,7 @@ void R_SetupSky( const char *skyboxname ) for( i = 0; i < 6; i++ ) { Q_snprintf( sidename, sizeof( sidename ), "%s%s", loadname, r_skyBoxSuffix[i] ); - tr.skyboxTextures[i] = GL_LoadTexture( sidename, NULL, 0, TF_CLAMP|TF_SKY ); + tr.skyboxTextures[i] = GL_LoadTexture( sidename, NULL, 0, TF_CLAMP|TF_SKY, NULL ); GL_SetTextureType( tr.skyboxTextures[i], TEX_CUBEMAP ); if( !tr.skyboxTextures[i] ) break; } @@ -457,7 +457,7 @@ void R_SetupSky( const char *skyboxname ) for( i = 0; i < 6; i++ ) { Q_snprintf( sidename, sizeof( sidename ), "%s_%s", loadname, r_skyBoxSuffix[i] ); - tr.skyboxTextures[i] = GL_LoadTexture( sidename, NULL, 0, TF_CLAMP|TF_SKY ); + tr.skyboxTextures[i] = GL_LoadTexture( sidename, NULL, 0, TF_CLAMP|TF_SKY, NULL ); GL_SetTextureType( tr.skyboxTextures[i], TEX_CUBEMAP ); if( !tr.skyboxTextures[i] ) break; } diff --git a/engine/common/common.h b/engine/common/common.h index c6dd47e5..eda05b2b 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -474,6 +474,16 @@ typedef enum IMAGE_REMAP = BIT(27), // interpret width and height as top and bottom color } imgFlags_t; +// ordering is important! +typedef enum +{ + BLUR_FILTER = 0, + BLUR_FILTER2, + EDGE_FILTER, + EMBOSS_FILTER, + NUM_FILTERS, +} pixfilter_t; + typedef struct rgbdata_s { word width; // image width @@ -487,6 +497,21 @@ typedef struct rgbdata_s size_t size; // for bounds checking } rgbdata_t; +// imgfilter processing flags +typedef enum +{ + FILTER_GRAYSCALE = BIT(0), +} flFlags_t; + +typedef struct imgfilter_s +{ + int filter; // pixfilter_t + float factor; // filter factor value + float bias; // filter bias value + flFlags_t flags; // filter additional flags + uint blendFunc; // blending mode +} imgfilter_t; + // // imagelib // @@ -497,7 +522,7 @@ qboolean FS_SaveImage( const char *filename, rgbdata_t *pix ); rgbdata_t *FS_CopyImage( rgbdata_t *in ); void FS_FreeImage( rgbdata_t *pack ); extern const bpc_desc_t PFDesc[]; // image get pixelformat -qboolean Image_Process( rgbdata_t **pix, int width, int height, float gamma, uint flags ); +qboolean Image_Process( rgbdata_t **pix, int width, int height, float gamma, uint flags, imgfilter_t *filter ); void Image_PaletteHueReplace( byte *palSrc, int newHue, int start, int end ); void Image_SetForceFlags( uint flags ); // set image force flags on loading diff --git a/engine/common/console.c b/engine/common/console.c index 250d1e5a..73ecf66d 100644 --- a/engine/common/console.c +++ b/engine/common/console.c @@ -439,7 +439,7 @@ static void Con_LoadConsoleFont( int fontNumber, cl_font_t *font ) if( font->valid ) return; // already loaded // loading conchars - font->hFontTexture = GL_LoadTexture( va( "fonts/font%i", fontNumber ), NULL, 0, TF_FONT|TF_NEAREST ); + font->hFontTexture = GL_LoadTexture( va( "fonts/font%i", fontNumber ), NULL, 0, TF_FONT|TF_NEAREST, NULL ); R_GetTextureParms( &fontWidth, NULL, font->hFontTexture ); // setup creditsfont @@ -1947,14 +1947,14 @@ void Con_VidInit( void ) if( scr_width->integer < 640 ) { if( FS_FileExists( "cached/conback400", false )) - con.background = GL_LoadTexture( "cached/conback400", NULL, 0, TF_IMAGE ); - else con.background = GL_LoadTexture( "cached/conback", NULL, 0, TF_IMAGE ); + con.background = GL_LoadTexture( "cached/conback400", NULL, 0, TF_IMAGE, NULL ); + else con.background = GL_LoadTexture( "cached/conback", NULL, 0, TF_IMAGE, NULL ); } else { if( FS_FileExists( "cached/conback640", false )) - con.background = GL_LoadTexture( "cached/conback640", NULL, 0, TF_IMAGE ); - else con.background = GL_LoadTexture( "cached/conback", NULL, 0, TF_IMAGE ); + con.background = GL_LoadTexture( "cached/conback640", NULL, 0, TF_IMAGE, NULL ); + else con.background = GL_LoadTexture( "cached/conback", NULL, 0, TF_IMAGE, NULL ); } } else @@ -1962,14 +1962,14 @@ void Con_VidInit( void ) if( scr_width->integer < 640 ) { if( FS_FileExists( "cached/loading400", false )) - con.background = GL_LoadTexture( "cached/loading400", NULL, 0, TF_IMAGE ); - else con.background = GL_LoadTexture( "cached/loading", NULL, 0, TF_IMAGE ); + con.background = GL_LoadTexture( "cached/loading400", NULL, 0, TF_IMAGE, NULL ); + else con.background = GL_LoadTexture( "cached/loading", NULL, 0, TF_IMAGE, NULL ); } else { if( FS_FileExists( "cached/loading640", false )) - con.background = GL_LoadTexture( "cached/loading640", NULL, 0, TF_IMAGE ); - else con.background = GL_LoadTexture( "cached/loading", NULL, 0, TF_IMAGE ); + con.background = GL_LoadTexture( "cached/loading640", NULL, 0, TF_IMAGE, NULL ); + else con.background = GL_LoadTexture( "cached/loading", NULL, 0, TF_IMAGE, NULL ); } } diff --git a/engine/common/imagelib/img_utils.c b/engine/common/imagelib/img_utils.c index d9da5ed7..3741fc80 100644 --- a/engine/common/imagelib/img_utils.c +++ b/engine/common/imagelib/img_utils.c @@ -16,10 +16,12 @@ GNU General Public License for more details. #include "imagelib.h" #include "mathlib.h" #include "mod_local.h" +#include "gl_export.h" convar_t *gl_round_down; #define LERPBYTE( i ) r = resamplerow1[i]; out[i] = (byte)(((( resamplerow2[i] - r ) * lerp)>>16 ) + r ) +#define FILTER_SIZE 5 uint d_8toQ1table[256]; uint d_8toHLtable[256]; @@ -81,6 +83,38 @@ static byte palette_hl[768] = 147,255,247,199,255,255,255,159,91,83 }; +static float FILTER[NUM_FILTERS][FILTER_SIZE][FILTER_SIZE] = +{ +{ // regular blur +{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, +{ 0.0f, 1.0f, 1.0f, 1.0f, 0.0f }, +{ 0.0f, 1.0f, 1.0f, 1.0f, 0.0f }, +{ 0.0f, 1.0f, 1.0f, 1.0f, 0.0f }, +{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, +}, +{ // light blur +{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, +{ 0.0f, 1.0f, 1.0f, 1.0f, 0.0f }, +{ 0.0f, 1.0f, 4.0f, 1.0f, 0.0f }, +{ 0.0f, 1.0f, 1.0f, 1.0f, 0.0f }, +{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, +}, +{ // find edges +{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, +{ 0.0f, -1.0f, -1.0f, -1.0f, 0.0f }, +{ 0.0f, -1.0f, 8.0f, -1.0f, 0.0f }, +{ 0.0f, -1.0f, -1.0f, -1.0f, 0.0f }, +{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, +}, +{ // emboss +{-0.7f, -0.7f, -0.7f, -0.7f, 0.0f }, +{-0.7f, -0.7f, -0.7f, 0.0f, 0.7f }, +{-0.7f, -0.7f, 0.0f, 0.7f, 0.7f }, +{-0.7f, 0.0f, 0.7f, 0.7f, 0.7f }, +{ 0.0f, 0.7f, 0.7f, 0.7f, 0.7f }, +} +}; + /* ============================================================================= @@ -1345,7 +1379,128 @@ qboolean Image_RemapInternal( rgbdata_t *pic, int topColor, int bottomColor ) return true; } -qboolean Image_Process( rgbdata_t **pix, int width, int height, float gamma, uint flags ) +/* +================== +Image_ApplyFilter + +Applies a 5 x 5 filtering matrix to the texture, then runs it through a simulated OpenGL texture environment +blend with the original data to derive a new texture. Freaky, funky, and *f--king* *fantastic*. You can do +reasonable enough "fake bumpmapping" with this baby... + +Filtering algorithm from http://www.student.kuleuven.ac.be/~m0216922/CG/filtering.html +All credit due +================== +*/ +qboolean Image_ApplyFilter( rgbdata_t *pic, int filter, float factor, float bias, flFlags_t flags, GLenum blendFunc ) +{ + int i, x, y; + uint *fin, *fout; + size_t size; + + // first expand the image into 32-bit buffer + pic = Image_DecompressInternal( pic ); + + size = image.width * image.height * 4; + image.tempbuffer = Mem_Realloc( host.imagepool, image.tempbuffer, size ); + fout = (uint *)image.tempbuffer; + fin = (uint *)pic->buffer; + + for( x = 0; x < image.width; x++ ) + { + for( y = 0; y < image.height; y++ ) + { + vec3_t vout = { 0.0f, 0.0f, 0.0f }; + int pos_x, pos_y; + + for( pos_x = 0; pos_x < FILTER_SIZE; pos_x++ ) + { + for( pos_y = 0; pos_y < FILTER_SIZE; pos_y++ ) + { + int img_x = (x - (FILTER_SIZE / 2) + pos_x + image.width) % image.width; + int img_y = (y - (FILTER_SIZE / 2) + pos_y + image.height) % image.height; + + // casting's a unary operation anyway, so the othermost set of brackets in the left part + // of the rvalue should not be necessary... but i'm paranoid when it comes to C... + vout[0] += ((float)((byte *)&fin[img_y * image.width + img_x])[0]) * FILTER[filter][pos_x][pos_y]; + vout[1] += ((float)((byte *)&fin[img_y * image.width + img_x])[1]) * FILTER[filter][pos_x][pos_y]; + vout[2] += ((float)((byte *)&fin[img_y * image.width + img_x])[2]) * FILTER[filter][pos_x][pos_y]; + } + } + + // multiply by factor, add bias, and clamp + for( i = 0; i < 3; i++ ) + { + vout[i] *= factor; + vout[i] += bias; + vout[i] = bound( 0.0f, vout[i], 255.0f ); + } + + if( flags & FILTER_GRAYSCALE ) + { + // NTSC greyscale conversion standard + float avg = (vout[0] * 30.0f + vout[1] * 59.0f + vout[2] * 11.0f) / 100.0f; + + // divide by 255 so GL operations work as expected + vout[0] = avg / 255.0f; + vout[1] = avg / 255.0f; + vout[2] = avg / 255.0f; + } + + // write to temp - first, write data in (to get the alpha channel quickly and + // easily, which will be left well alone by this particular operation...!) + fout[y * image.width + x] = fin[y * image.width + x]; + + // now write in each element, applying the blend operator. blend + // operators are based on standard OpenGL TexEnv modes, and the + // formulas are derived from the OpenGL specs (http://www.opengl.org). + for( i = 0; i < 3; i++ ) + { + // divide by 255 so GL operations work as expected + float src = ((float)((byte *)&fin[y * image.width + x])[i]) / 255.0f; + float tmp; + + switch( blendFunc ) + { + case GL_ADD: + tmp = vout[i] + src; + break; + case GL_BLEND: + // default is FUNC_ADD here + // CsS + CdD works out as Src * Dst * 2 + tmp = vout[i] * src * 2.0f; + break; + case GL_DECAL: + // same as GL_REPLACE unless there's alpha, which we ignore for this + case GL_REPLACE: + tmp = vout[i]; + break; + case GL_ADD_SIGNED: + tmp = (vout[i] + src) - 0.5f; + break; + case GL_MODULATE: + default: // same as default + tmp = vout[i] * src; + break; + } + + // multiply back by 255 to get the proper byte scale + tmp *= 255.0f; + + // bound the temp target again now, cos the operation may have thrown it out + tmp = bound( 0.0f, tmp, 255.0f ); + // and copy it in + ((byte *)&fout[y * image.width + x])[i] = (byte)tmp; + } + } + } + + // copy result back + Q_memcpy( fin, fout, size ); + + return true; +} + +qboolean Image_Process( rgbdata_t **pix, int width, int height, float gamma, uint flags, imgfilter_t *filter ) { rgbdata_t *pic = *pix; qboolean result = true; @@ -1359,7 +1514,7 @@ qboolean Image_Process( rgbdata_t **pix, int width, int height, float gamma, uin return false; } - if( !flags ) + if( !flags && !filter ) { // clear any force flags image.force_flags = 0; @@ -1384,6 +1539,8 @@ qboolean Image_Process( rgbdata_t **pix, int width, int height, float gamma, uin if( flags & IMAGE_FORCE_RGBA ) pic = Image_DecompressInternal( pic ); if( flags & IMAGE_LIGHTGAMMA ) pic = Image_LightGamma( pic, gamma ); + if( filter ) Image_ApplyFilter( pic, filter->filter, filter->factor, filter->bias, filter->flags, filter->blendFunc ); + // quantize image if( flags & IMAGE_QUANTIZE ) pic = Image_Quantize( pic ); diff --git a/engine/common/mod_studio.c b/engine/common/mod_studio.c index 5d0e79ce..4479370c 100644 --- a/engine/common/mod_studio.c +++ b/engine/common/mod_studio.c @@ -229,7 +229,7 @@ hull_t *Mod_HullForStudio( model_t *model, float frame, int sequence, vec3_t ang VectorCopy( angles, angles2 ); angles2[PITCH] = -angles2[PITCH]; // stupid quake bug - pBlendAPI->SV_StudioSetupBones( model, frame, sequence, angles2, origin, pcontroller, pblending, -1, pEdict ); + pBlendAPI->SV_StudioSetupBones( model, frame, sequence, angles2, origin, pcontroller, pblending, pEdict, -1 ); phitbox = (mstudiobbox_t *)((byte *)mod_studiohdr + mod_studiohdr->hitboxindex); for( i = j = 0; i < mod_studiohdr->numhitboxes; i++, j += 6 ) @@ -626,7 +626,7 @@ NOTE: pEdict is unused ==================== */ static void SV_StudioSetupBones( model_t *pModel, float frame, int sequence, const vec3_t angles, const vec3_t origin, - const byte *pcontroller, const byte *pblending, int iBone, const edict_t *pEdict ) + const byte *pcontroller, const byte *pblending, const edict_t *pEdict, int iBone ) { int i, j, numbones = 0; int boneused[MAXSTUDIOBONES]; @@ -749,7 +749,7 @@ void Mod_StudioGetAttachment( const edict_t *e, int iAttachment, float *origin, angles2[PITCH] = -angles2[PITCH]; pBlendAPI->SV_StudioSetupBones( mod, e->v.frame, e->v.sequence, angles2, e->v.origin, - e->v.controller, e->v.blending, pAtt[iAttachment].bone, e ); + e->v.controller, e->v.blending, e, pAtt[iAttachment].bone ); // compute pos and angles if( origin != NULL ) @@ -780,7 +780,7 @@ void Mod_GetBonePosition( const edict_t *e, int iBone, float *origin, float *ang if( !mod_studiohdr ) return; pBlendAPI->SV_StudioSetupBones( mod, e->v.frame, e->v.sequence, e->v.angles, e->v.origin, - e->v.controller, e->v.blending, iBone, e ); + e->v.controller, e->v.blending, e, iBone ); if( origin ) Matrix3x4_OriginFromMatrix( studio_bones[iBone], origin ); if( angles ) VectorAngles( studio_bones[iBone][0], angles ); // bone forward to angles diff --git a/engine/common/model.c b/engine/common/model.c index 9b89e93d..b0d1b65e 100644 --- a/engine/common/model.c +++ b/engine/common/model.c @@ -612,6 +612,7 @@ static void Mod_LoadTextures( const dlump_t *l ) texture_t *altanims[10]; int num, max, altmax; char texname[64]; + imgfilter_t *filter; mip_t *mt; int i, j; @@ -669,6 +670,7 @@ static void Mod_LoadTextures( const dlump_t *l ) // convert to lowercase Q_strnlwr( mt->name, mt->name, sizeof( mt->name )); Q_strncpy( tx->name, mt->name, sizeof( tx->name )); + filter = R_FindTexFilter( tx->name ); // grab texture filter tx->width = mt->width; tx->height = mt->height; @@ -693,7 +695,7 @@ static void Mod_LoadTextures( const dlump_t *l ) if( load_external ) { - tr.solidskyTexture = GL_LoadTexture( texname, NULL, 0, TF_UNCOMPRESSED|TF_NOMIPMAP ); + tr.solidskyTexture = GL_LoadTexture( texname, NULL, 0, TF_UNCOMPRESSED|TF_NOMIPMAP, NULL ); GL_SetTextureType( tr.solidskyTexture, TEX_BRUSH ); load_external = false; } @@ -715,7 +717,7 @@ static void Mod_LoadTextures( const dlump_t *l ) if( load_external ) { - tr.alphaskyTexture = GL_LoadTexture( texname, NULL, 0, TF_UNCOMPRESSED|TF_NOMIPMAP ); + tr.alphaskyTexture = GL_LoadTexture( texname, NULL, 0, TF_UNCOMPRESSED|TF_NOMIPMAP, NULL ); GL_SetTextureType( tr.alphaskyTexture, TEX_BRUSH ); load_external = false; } @@ -767,12 +769,12 @@ load_wad_textures: int size = (int)sizeof( mip_t ) + ((mt->width * mt->height * 85)>>6); if( bmodel_version == HLBSP_VERSION ) size += sizeof( short ) + 768; - tx->gl_texturenum = GL_LoadTexture( texname, (byte *)mt, size, 0 ); + tx->gl_texturenum = GL_LoadTexture( texname, (byte *)mt, size, 0, filter ); } else { // okay, loading it from wad - tx->gl_texturenum = GL_LoadTexture( texname, NULL, 0, 0 ); + tx->gl_texturenum = GL_LoadTexture( texname, NULL, 0, 0, filter ); if( !tx->gl_texturenum && load_external ) { @@ -817,7 +819,7 @@ load_wad_textures: int size = (int)sizeof( mip_t ) + ((mt->width * mt->height * 85)>>6); if( bmodel_version == HLBSP_VERSION ) size += sizeof( short ) + 768; - tx->fb_texturenum = GL_LoadTexture( texname, (byte *)mt, size, TF_NOMIPMAP|TF_MAKELUMA ); + tx->fb_texturenum = GL_LoadTexture( texname, (byte *)mt, size, TF_NOMIPMAP|TF_MAKELUMA, NULL ); } else { @@ -830,7 +832,7 @@ load_wad_textures: if( !load_external_luma ) src = FS_LoadFile( va( "%s.mip", tx->name ), &srcSize, false ); // okay, loading it from wad or hi-res version - tx->fb_texturenum = GL_LoadTexture( texname, src, srcSize, TF_NOMIPMAP|TF_MAKELUMA ); + tx->fb_texturenum = GL_LoadTexture( texname, src, srcSize, TF_NOMIPMAP|TF_MAKELUMA, NULL ); if( src ) Mem_Free( src ); if( !tx->fb_texturenum && load_external_luma )