diff --git a/src/engine/client/cg_api.h b/src/engine/client/cg_api.h index c271511c37..f6bc84c296 100644 --- a/src/engine/client/cg_api.h +++ b/src/engine/client/cg_api.h @@ -121,7 +121,7 @@ void trap_S_StartBackgroundTrack( const char *intro, const char *loop void trap_R_LoadWorldMap( const char *mapname ); qhandle_t trap_R_RegisterModel( const char *name ); qhandle_t trap_R_RegisterSkin( const char *name ); -qhandle_t trap_R_RegisterShader( const char *name, RegisterShaderFlags_t flags ); +qhandle_t trap_R_RegisterShader( const char *name, int flags ); void trap_R_ClearScene(); void trap_R_AddRefEntityToScene( const refEntity_t *re ); diff --git a/src/engine/client/cl_cgame.cpp b/src/engine/client/cl_cgame.cpp index 3a8843e571..23f9325064 100644 --- a/src/engine/client/cl_cgame.cpp +++ b/src/engine/client/cl_cgame.cpp @@ -1259,7 +1259,7 @@ void CGameVM::QVMSyscall(int syscallNum, Util::Reader& reader, IPC::Channel& cha case CG_R_REGISTERSHADER: IPC::HandleMsg(channel, std::move(reader), [this] (const std::string& name, int flags, int& handle) { - handle = re.RegisterShader(name.c_str(), (RegisterShaderFlags_t) flags); + handle = re.RegisterShader(name.c_str(), flags); }); break; diff --git a/src/engine/null/null_renderer.cpp b/src/engine/null/null_renderer.cpp index 9c1876d2f3..ca96c51d3d 100644 --- a/src/engine/null/null_renderer.cpp +++ b/src/engine/null/null_renderer.cpp @@ -46,7 +46,7 @@ qhandle_t RE_RegisterSkin( const char *name ) { return FS_FOpenFileRead( name, nullptr ); } -qhandle_t RE_RegisterShader( const char *, RegisterShaderFlags_t ) +qhandle_t RE_RegisterShader( const char *, int ) { return 1; } diff --git a/src/engine/renderer/InternalImage.cpp b/src/engine/renderer/InternalImage.cpp index 39ba422e98..d0c870b059 100644 --- a/src/engine/renderer/InternalImage.cpp +++ b/src/engine/renderer/InternalImage.cpp @@ -105,6 +105,40 @@ int R_GetImageCustomScalingStep( const image_t *image, const imageParams_t &imag return 0; } + // Consider the larger edge as the "image dimension" + int scaledDimension = std::max( image->width, image->height ); + + int scalingStep = 0; + + // Scale down the image size according to the screen size. + if ( image->bits & IF_FITSCREEN ) + { + int largerSide = std::max( glConfig.vidWidth, glConfig.vidHeight ); + + if ( scaledDimension > largerSide ) + { + while ( scaledDimension > largerSide ) + { + scaledDimension >>= 1; + scalingStep++; + } + + /* With r_imageFitScreen == 1, we need the larger image size before + it becomes smaller than screen. + + With r_imageFitScreen == 2 the image is never larger than screen, as + we allow the larger size that is not larger than screen, it can be the + larger size smaller than screen. */ + if ( scaledDimension != largerSide && r_imageFitScreen.Get() != 2 ) + { + scaledDimension <<= 1; + scalingStep--; + } + } + + return scalingStep; + } + int materialMinDimension = r_ignoreMaterialMinDimension->integer ? 0 : imageParams.minDimension; int materialMaxDimension = r_ignoreMaterialMaxDimension->integer ? 0 : imageParams.maxDimension; @@ -130,10 +164,6 @@ int R_GetImageCustomScalingStep( const image_t *image, const imageParams_t &imag maxDimension = std::min( maxDimension, r_imageMaxDimension->integer ); } - // Consider the larger edge as the "image dimension" - int scaledDimension = std::max( image->width, image->height ); - int scalingStep = 0; - // 1st priority: scaledDimension >= minDimension // 2nd priority: scaledDimension <= maxDimension // 3rd priority: scalingStep >= r_picMip->integer diff --git a/src/engine/renderer/tr_image.cpp b/src/engine/renderer/tr_image.cpp index 077b42e100..91330ff001 100644 --- a/src/engine/renderer/tr_image.cpp +++ b/src/engine/renderer/tr_image.cpp @@ -763,6 +763,14 @@ void R_UploadImage( const byte **dataArray, int numLayers, int numMips, image_t int scaledWidth = image->width; int scaledHeight = image->height; + + // If r_imageFitScreen is disabled, use nopicmip instead. + if ( ( image->bits & IF_FITSCREEN ) && !r_imageFitScreen.Get() ) + { + image->bits &= ~IF_FITSCREEN; + image->bits |= IF_NOPICMIP; + } + int customScalingStep = R_GetImageCustomScalingStep( image, imageParams ); R_DownscaleImageDimensions( customScalingStep, &scaledWidth, &scaledHeight, &dataArray, numLayers, &numMips ); diff --git a/src/engine/renderer/tr_init.cpp b/src/engine/renderer/tr_init.cpp index de118a313a..47c3fc3ad9 100644 --- a/src/engine/renderer/tr_init.cpp +++ b/src/engine/renderer/tr_init.cpp @@ -150,6 +150,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA cvar_t *r_ignoreMaterialMinDimension; cvar_t *r_ignoreMaterialMaxDimension; cvar_t *r_replaceMaterialMinDimensionIfPresentWithMaxDimension; + Cvar::Range> r_imageFitScreen("r_imageFitScreen", "downscale “fitscreen” images to fit the screen size: 0: disable, 1: downscale as much as possible without being smaller than screen size (default), 2: downscale to never be larger then screen size", Cvar::NONE, 1, 0, 2); cvar_t *r_finish; cvar_t *r_clear; cvar_t *r_textureMode; @@ -1095,6 +1096,7 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p r_ignoreMaterialMaxDimension = Cvar_Get( "r_ignoreMaterialMaxDimension", "0", CVAR_LATCH | CVAR_ARCHIVE ); r_replaceMaterialMinDimensionIfPresentWithMaxDimension = Cvar_Get( "r_replaceMaterialMinDimensionIfPresentWithMaxDimension", "0", CVAR_LATCH | CVAR_ARCHIVE ); + Cvar::Latch(r_imageFitScreen); r_colorbits = Cvar_Get( "r_colorbits", "0", CVAR_LATCH ); r_mode = Cvar_Get( "r_mode", "-2", CVAR_LATCH | CVAR_ARCHIVE ); r_customwidth = Cvar_Get( "r_customwidth", "1600", CVAR_LATCH | CVAR_ARCHIVE ); diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index 9c772415a9..e56d815d09 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -537,6 +537,7 @@ enum class dynamicLightRenderer_t { LEGACY, TILED }; { IF_NONE, IF_NOPICMIP = BIT( 0 ), + IF_FITSCREEN = BIT( 1 ), IF_NORMALMAP = BIT( 2 ), IF_RGBA16F = BIT( 3 ), IF_RGBA32F = BIT( 4 ), @@ -1147,6 +1148,7 @@ enum class dynamicLightRenderer_t { LEGACY, TILED }; int deformIndex; bool overrideNoPicMip; // for images that must always be full resolution + bool fitScreen; // For images that should be scaled to fit the screen size. bool overrideFilterType; // for console fonts, 2D elements, etc. filterType_t filterType; bool overrideWrapType; @@ -1282,6 +1284,7 @@ enum class dynamicLightRenderer_t { LEGACY, TILED }; float polygonOffsetValue; bool noPicMip; // for images that must always be full resolution + bool fitScreen; // For images that should be scaled to fit the screen size. int imageMinDimension; // for images that must not be loaded with smaller size int imageMaxDimension; // for images that must not be loaded with larger size filterType_t filterType; // for console fonts, 2D elements, etc. @@ -2906,6 +2909,7 @@ enum class dynamicLightRenderer_t { LEGACY, TILED }; extern cvar_t *r_ignoreMaterialMinDimension; extern cvar_t *r_ignoreMaterialMaxDimension; extern cvar_t *r_replaceMaterialMinDimensionIfPresentWithMaxDimension; + extern Cvar::Range> r_imageFitScreen; extern cvar_t *r_finish; extern cvar_t *r_drawBuffer; extern cvar_t *r_textureMode; @@ -3263,12 +3267,11 @@ inline bool checkGLErrors() ==================================================================== */ - qhandle_t RE_RegisterShader( const char *name, RegisterShaderFlags_t flags ); + qhandle_t RE_RegisterShader( const char *name, int flags ); qhandle_t RE_RegisterShaderFromImage( const char *name, image_t *image ); bool RE_LoadDynamicShader( const char *shadername, const char *shadertext ); - shader_t *R_FindShader( const char *name, shaderType_t type, - RegisterShaderFlags_t flags ); + shader_t *R_FindShader( const char *name, shaderType_t type, int flags ); shader_t *R_GetShaderByHandle( qhandle_t hShader ); shader_t *R_FindShaderByName( const char *name ); const char *RE_GetShaderNameFromHandle( qhandle_t shader ); diff --git a/src/engine/renderer/tr_public.h b/src/engine/renderer/tr_public.h index a079d37e5d..578f1f29c5 100644 --- a/src/engine/renderer/tr_public.h +++ b/src/engine/renderer/tr_public.h @@ -130,8 +130,7 @@ struct refexport_t qhandle_t ( *RegisterModel )( const char *name ); //qhandle_t (*RegisterModelAllLODs) (const char *name); qhandle_t ( *RegisterSkin )( const char *name ); - qhandle_t ( *RegisterShader )( const char *name, - RegisterShaderFlags_t flags ); + qhandle_t ( *RegisterShader )( const char *name, int flags ); fontInfo_t* ( *RegisterFont )( const char *fontName, int pointSize ); void ( *UnregisterFont )( fontInfo_t *font ); void ( *Glyph )( fontInfo_t *font, const char *str, glyphInfo_t *glyph ); diff --git a/src/engine/renderer/tr_shader.cpp b/src/engine/renderer/tr_shader.cpp index 1af486e9b3..11a1c12409 100644 --- a/src/engine/renderer/tr_shader.cpp +++ b/src/engine/renderer/tr_shader.cpp @@ -1479,6 +1479,11 @@ static bool LoadMap( shaderStage_t *stage, const char *buffer, stageType_t type, imageParams.bits |= IF_NOPICMIP; } + if ( stage->fitScreen || shader.fitScreen ) + { + imageParams.bits |= IF_FITSCREEN; + } + switch ( type ) { case stageType_t::ST_NORMALMAP: @@ -2173,6 +2178,11 @@ static bool ParseStage( shaderStage_t *stage, const char **text ) imageBits |= IF_NOPICMIP; } + if ( stage->fitScreen || shader.fitScreen ) + { + imageBits |= IF_FITSCREEN; + } + if ( stage->overrideFilterType ) { filterType = stage->filterType; @@ -2294,6 +2304,11 @@ static bool ParseStage( shaderStage_t *stage, const char **text ) imageBits |= IF_NOPICMIP; } + if ( stage->fitScreen || shader.fitScreen ) + { + imageBits |= IF_FITSCREEN; + } + if ( stage->overrideFilterType ) { filterType = stage->filterType; @@ -2386,6 +2401,10 @@ static bool ParseStage( shaderStage_t *stage, const char **text ) { stage->overrideNoPicMip = true; } + else if ( !Q_stricmp( token, "fitScreen" ) ) + { + stage->fitScreen = true; + } // clamp, edgeClamp etc. else if ( ParseClampType( token, &stage->wrapType ) ) { @@ -4178,6 +4197,12 @@ static bool ParseShader( const char *_text ) shader.noPicMip = true; continue; } + // fit screen adjustment + else if ( !Q_stricmp( token, "fitScreen" ) ) + { + shader.fitScreen = true; + continue; + } // imageMinDimension enforcement else if ( !Q_stricmp( token, "imageMinDimension" ) ) { @@ -6218,8 +6243,7 @@ the vertex rgba modulate values, as appropriate for misc_model pre-lit surfaces. =============== */ -shader_t *R_FindShader( const char *name, shaderType_t type, - RegisterShaderFlags_t flags ) +shader_t *R_FindShader( const char *name, shaderType_t type, int flags ) { char strippedName[ MAX_QPATH ]; char fileName[ MAX_QPATH ]; @@ -6327,32 +6351,43 @@ shader_t *R_FindShader( const char *name, shaderType_t type, // if not defined in the in-memory shader descriptions, // look for a single supported image file bits = IF_NONE; - if( flags & RSF_NOMIP ) + + if ( flags & RSF_NOMIP ) + { bits |= IF_NOPICMIP; - else shader.noPicMip = true; + } + + if ( flags & RSF_FITSCREEN ) + { + bits |= IF_FITSCREEN; + shader.fitScreen = true; + } - if( flags & RSF_NOLIGHTSCALE ) + if ( flags & RSF_NOLIGHTSCALE ) + { bits |= IF_NOLIGHTSCALE; + } Log::Debug( "loading '%s' image as shader", fileName ); - // choosing filter based on the NOMIP flag seems strange, - // maybe it should be changed to type == SHADER_2D - if( !(bits & RSF_NOMIP) ) { - LoadExtraMaps( &stages[ 0 ], fileName ); - + if( bits & RSF_2D ) + { imageParams_t imageParams = {}; imageParams.bits = bits; - imageParams.filterType = filterType_t::FT_DEFAULT; - imageParams.wrapType = wrapTypeEnum_t::WT_REPEAT; + imageParams.filterType = filterType_t::FT_LINEAR; + imageParams.wrapType = wrapTypeEnum_t::WT_CLAMP; image = R_FindImageFile( fileName, imageParams ); - } else { + } + else + { + LoadExtraMaps( &stages[ 0 ], fileName ); + imageParams_t imageParams = {}; imageParams.bits = bits; - imageParams.filterType = filterType_t::FT_LINEAR; - imageParams.wrapType = wrapTypeEnum_t::WT_CLAMP; + imageParams.filterType = filterType_t::FT_DEFAULT; + imageParams.wrapType = wrapTypeEnum_t::WT_REPEAT; image = R_FindImageFile( fileName, imageParams ); } @@ -6492,7 +6527,7 @@ This should really only be used for explicit shaders, because there is no way to ask for different implicit lighting modes (vertex, lightmap, etc) ==================== */ -qhandle_t RE_RegisterShader( const char *name, RegisterShaderFlags_t flags ) +qhandle_t RE_RegisterShader( const char *name, int flags ) { shader_t *sh; diff --git a/src/engine/renderer/tr_types.h b/src/engine/renderer/tr_types.h index 610db62ff7..31e06871c5 100644 --- a/src/engine/renderer/tr_types.h +++ b/src/engine/renderer/tr_types.h @@ -94,11 +94,13 @@ using bool8_t = uint8_t; using glIndex_t = unsigned int; enum RegisterShaderFlags_t { - RSF_DEFAULT = 0x00, - RSF_NOMIP = 0x01, - RSF_LIGHT_ATTENUATION = 0x02, - RSF_NOLIGHTSCALE = 0x04, - RSF_SPRITE = 0x08 + RSF_DEFAULT = BIT( 0 ), + RSF_2D = BIT( 1 ), + RSF_NOMIP = BIT( 2 ), + RSF_FITSCREEN = BIT( 3 ), + RSF_LIGHT_ATTENUATION = BIT( 4 ), + RSF_NOLIGHTSCALE = BIT( 5 ), + RSF_SPRITE = BIT( 6 ), }; struct polyVert_t diff --git a/src/shared/client/cg_api.cpp b/src/shared/client/cg_api.cpp index 5c8b016864..db21a8ecef 100644 --- a/src/shared/client/cg_api.cpp +++ b/src/shared/client/cg_api.cpp @@ -317,7 +317,7 @@ qhandle_t trap_R_RegisterSkin( const char *name ) return handle; } -qhandle_t trap_R_RegisterShader( const char *name, RegisterShaderFlags_t flags ) +qhandle_t trap_R_RegisterShader( const char *name, int flags ) { int handle; VM::SendMsg(name, flags, handle);