mirror of
https://github.com/dhewm/dhewm3.git
synced 2025-04-01 23:41:07 +00:00
Merge pull request #661 from DanielGibson/bc7
Support BPTC (BC7) -compressed .dds textures.
This commit is contained in:
commit
b0e3f59c07
9 changed files with 157 additions and 9 deletions
15
Changelog.md
15
Changelog.md
|
@ -8,7 +8,20 @@ Note: Numbers starting with a "#" like #330 refer to the bugreport with that num
|
|||
------------------------------------------------------------------------
|
||||
|
||||
* Enable/disable Soft Particles when **loading** a graphics quality preset (only enabled in Ultra preset,
|
||||
though you can still configure it independently as before; #604)
|
||||
though you can still configure it independently like before; #604)
|
||||
* Support BC7-compressed (BPTC) .dds textures. They offer better quality than the older S3TC/DXT/BC1-3
|
||||
texture compression standard that Doom3 always supported. Mostly relevant for high-res retexturing
|
||||
packs, because they offer similar quality as uncompressed TGAs while being smaller, using only
|
||||
a quarter of the VRAM (TGA: 4 bytes per pixel, BC7: 1 byte per pixel) and loading *significantly*
|
||||
faster because mipmaps are contained and don't have to be generated on load.
|
||||
If you have such DDS files and want to use them (instead of TGAs), you must set
|
||||
`image_usePrecompressedTextures 1` and `image_useNormalCompression 1`.
|
||||
If you want to *create* .dds files with BC7 texture data, you can use any common texture compression
|
||||
tool, **except** for **normalmaps**, those must be created with my [**customized bc7enc**](https://github.com/DanielGibson/bc7enc_rdo)
|
||||
with the `-r2a` flag! *(Because Doom3 requires that normalmaps have the red channel moved into the
|
||||
alpha channel, id confusingly called that "RXGB", and AFAIK no other tool supports that for BC7.)*
|
||||
Just like the old DXT .dds files, they must be in the `dds/` subdirectory of a mod (either directly
|
||||
in the filesystem or in a .pk4).
|
||||
* Support SDL3 (SDL2 and, to some degree, SDL1.2 are also still supported)
|
||||
* Fix bugs on 64bit Big Endian platforms (#472, #625)
|
||||
* Fixes for high-poly models (use heap allocation instead of `alloca()` for big buffers; #528)
|
||||
|
|
|
@ -1463,7 +1463,7 @@ void Com_ExecMachineSpec_f( const idCmdArgs &args ) {
|
|||
cvarSystem->SetCVarInteger( "s_maxSoundsPerShader", 0, CVAR_ARCHIVE );
|
||||
cvarSystem->SetCVarInteger( "image_useNormalCompression", 0, CVAR_ARCHIVE );
|
||||
if ( !nores ) // DG: added optional "nores" argument
|
||||
cvarSystem->SetCVarInteger( "", 4, CVAR_ARCHIVE );
|
||||
cvarSystem->SetCVarInteger( "r_mode", 4, CVAR_ARCHIVE );
|
||||
cvarSystem->SetCVarInteger( "r_multiSamples", 0, CVAR_ARCHIVE );
|
||||
} else if ( com_machineSpec.GetInteger() == 1 ) { // medium
|
||||
cvarSystem->SetCVarString( "image_filter", "GL_LINEAR_MIPMAP_LINEAR", CVAR_ARCHIVE );
|
||||
|
|
|
@ -1706,6 +1706,8 @@ static int initialMode = 0;
|
|||
static int initialCustomVidRes[2];
|
||||
static int initialMSAAmode = 0;
|
||||
static int qualityPreset = 0;
|
||||
static bool initialUsePrecomprTextures = false;
|
||||
static int initialUseNormalCompr = false;
|
||||
|
||||
static void SetVideoStuffFromCVars()
|
||||
{
|
||||
|
@ -1734,6 +1736,9 @@ static void SetVideoStuffFromCVars()
|
|||
if ( qualityPreset == -1 )
|
||||
qualityPreset = 1; // default to medium Quality
|
||||
}
|
||||
|
||||
initialUsePrecomprTextures = globalImages->image_usePrecompressedTextures.GetBool();
|
||||
initialUseNormalCompr = globalImages->image_useNormalCompression.GetInteger();
|
||||
}
|
||||
|
||||
static bool VideoHasResettableChanges()
|
||||
|
@ -1754,6 +1759,12 @@ static bool VideoHasResettableChanges()
|
|||
if ( initialMSAAmode != r_multiSamples.GetInteger() ) {
|
||||
return true;
|
||||
}
|
||||
if ( initialUsePrecomprTextures != globalImages->image_usePrecompressedTextures.GetBool() ) {
|
||||
return true;
|
||||
}
|
||||
if ( initialUseNormalCompr != globalImages->image_useNormalCompression.GetInteger() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -1775,13 +1786,29 @@ static bool VideoHasApplyableChanges()
|
|||
return true;
|
||||
}
|
||||
|
||||
if ( initialUsePrecomprTextures != globalImages->image_usePrecompressedTextures.GetBool() ) {
|
||||
return true;
|
||||
}
|
||||
// Note: value of image_useNormalCompression is only relevant if image_usePrecompressedTextures is enabled
|
||||
if ( initialUsePrecomprTextures
|
||||
&& initialUseNormalCompr != globalImages->image_useNormalCompression.GetInteger() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static void ApplyVideoSettings()
|
||||
{
|
||||
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "vid_restart partial\n" );
|
||||
const char* cmd = "vid_restart partial\n";
|
||||
if ( initialUsePrecomprTextures != globalImages->image_usePrecompressedTextures.GetBool()
|
||||
|| initialUseNormalCompr != globalImages->image_useNormalCompression.GetInteger() )
|
||||
{
|
||||
// these need a full restart (=> textures must be reloaded)
|
||||
cmd = "vid_restart\n";
|
||||
}
|
||||
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, cmd );
|
||||
}
|
||||
|
||||
static void VideoResetChanges()
|
||||
|
@ -1794,6 +1821,8 @@ static void VideoResetChanges()
|
|||
r_fullscreenDesktop.SetBool( initialFullscreenDesktop );
|
||||
|
||||
r_multiSamples.SetInteger( initialMSAAmode );
|
||||
globalImages->image_usePrecompressedTextures.SetBool( initialUsePrecomprTextures );
|
||||
globalImages->image_useNormalCompression.SetInteger( initialUseNormalCompr );
|
||||
}
|
||||
|
||||
static void InitVideoOptionsMenu()
|
||||
|
@ -1937,6 +1966,36 @@ static void DrawVideoOptionsMenu()
|
|||
}
|
||||
AddCVarOptionTooltips( r_multiSamples, "Note: Not all GPUs/drivers support all modes, esp. not 16x!" );
|
||||
|
||||
bool usePreComprTex = globalImages->image_usePrecompressedTextures.GetBool();
|
||||
if ( ImGui::Checkbox( "Use precompressed textures", &usePreComprTex ) ) {
|
||||
globalImages->image_usePrecompressedTextures.SetBool(usePreComprTex);
|
||||
// by default I guess people also want compressed normal maps when using this
|
||||
// especially relevant for retexturing packs that only ship BC7 DDS files
|
||||
// (otherwise the lowres TGA normalmaps would be used)
|
||||
if ( usePreComprTex ) {
|
||||
cvarSystem->SetCVarInteger( "image_useNormalCompression", 2 );
|
||||
}
|
||||
}
|
||||
const char* descr = "Use precompressed (.dds) textures. Faster loading, use less VRAM, possibly worse image quality.\n"
|
||||
"May also be used by highres retexturing packs for BC7-compressed textures (there image quality is not impaired)";
|
||||
AddCVarOptionTooltips( globalImages->image_usePrecompressedTextures, descr );
|
||||
|
||||
ImGui::BeginDisabled( !usePreComprTex );
|
||||
bool useNormalCompr = globalImages->image_useNormalCompression.GetBool();
|
||||
ImGui::Dummy( ImVec2(16, 0) );
|
||||
ImGui::SameLine();
|
||||
if ( ImGui::Checkbox( "Use precompressed normalmaps", &useNormalCompr ) ) {
|
||||
// image_useNormalCompression 1 is not supported by modern GPUs
|
||||
globalImages->image_useNormalCompression.SetInteger(useNormalCompr ? 2 : 0);
|
||||
}
|
||||
if ( usePreComprTex ) {
|
||||
const char* descr = "Also use precompressed textures for normalmaps";
|
||||
AddCVarOptionTooltips( globalImages->image_useNormalCompression, descr );
|
||||
} else {
|
||||
AddTooltip( "Can only be used if precompressed textures are enabled!" );
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
// Apply Button
|
||||
if ( !VideoHasApplyableChanges() ) {
|
||||
ImGui::BeginDisabled();
|
||||
|
|
|
@ -125,6 +125,23 @@ typedef struct
|
|||
unsigned int dwReserved2[3];
|
||||
} ddsFileHeader_t;
|
||||
|
||||
// DG: additional header that's right behind the ddsFileHeader_t
|
||||
// ONLY IF ddsHeader.ddspf.dwFourCC == 'DX10'
|
||||
// https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dds-header-dxt10
|
||||
typedef struct
|
||||
{
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format
|
||||
unsigned int dxgiFormat; // we only support DXGI_FORMAT_BC7_UNORM = 98;
|
||||
// we *could* probably support DXGI_FORMAT_BC1_UNORM = 71, DXGI_FORMAT_BC2_UNORM = 74, DXGI_FORMAT_BC3_UNORM = 77
|
||||
// and map that to the old S3TC stuff, but I hope that tools writing those formats
|
||||
// stick to just DX9-style ddsFileHeader_t to be more compatible?
|
||||
|
||||
unsigned int resourceDimension; // 0: unknown, 2: Texture1D, 3: Texture2D, 4: Texture3D
|
||||
unsigned int miscFlag; // 4 if 2D texture is cubemap, else 0
|
||||
unsigned int arraySize; // number of elements in texture array
|
||||
unsigned int miscFlags2; // must be 0 for DX10, for DX11 has info about alpha channel (in lower 3 bits)
|
||||
} ddsDXT10addHeader_t;
|
||||
|
||||
|
||||
// increasing numeric values imply more information is stored
|
||||
typedef enum {
|
||||
|
|
|
@ -1217,7 +1217,7 @@ void R_ListImages_f( const idCmdArgs &args ) {
|
|||
|
||||
if ( uncompressedOnly ) {
|
||||
if ( ( image->internalFormat >= GL_COMPRESSED_RGB_S3TC_DXT1_EXT && image->internalFormat <= GL_COMPRESSED_RGBA_S3TC_DXT5_EXT )
|
||||
|| image->internalFormat == GL_COLOR_INDEX8_EXT ) {
|
||||
|| image->internalFormat == GL_COLOR_INDEX8_EXT || image->internalFormat == GL_COMPRESSED_RGBA_BPTC_UNORM_ARB ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,8 +37,9 @@ PROBLEM: compressed textures may break the zero clamp rule!
|
|||
*/
|
||||
|
||||
static bool FormatIsDXT( int internalFormat ) {
|
||||
if ( internalFormat < GL_COMPRESSED_RGB_S3TC_DXT1_EXT
|
||||
|| internalFormat > GL_COMPRESSED_RGBA_S3TC_DXT5_EXT ) {
|
||||
if ( (internalFormat < GL_COMPRESSED_RGB_S3TC_DXT1_EXT
|
||||
|| internalFormat > GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
|
||||
&& internalFormat != GL_COMPRESSED_RGBA_BPTC_UNORM ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -86,6 +87,8 @@ int idImage::BitsForInternalFormat( int internalFormat ) const {
|
|||
return 8;
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
||||
return 8;
|
||||
case GL_COMPRESSED_RGBA_BPTC_UNORM:
|
||||
return 8;
|
||||
case GL_RGBA4:
|
||||
return 16;
|
||||
case GL_RGB5:
|
||||
|
@ -1368,7 +1371,7 @@ bool idImage::CheckPrecompressedImage( bool fullLoad ) {
|
|||
}
|
||||
|
||||
int len = f->Length();
|
||||
if ( len < sizeof( ddsFileHeader_t ) ) {
|
||||
if ( len < sizeof( ddsFileHeader_t ) + 4 ) { // +4 for the magic 'DDS ' fourcc at the beginning
|
||||
fileSystem->CloseFile( f );
|
||||
return false;
|
||||
}
|
||||
|
@ -1392,6 +1395,7 @@ bool idImage::CheckPrecompressedImage( bool fullLoad ) {
|
|||
unsigned int magic = LittleInt( *(unsigned int *)data );
|
||||
ddsFileHeader_t *_header = (ddsFileHeader_t *)(data + 4);
|
||||
int ddspf_dwFlags = LittleInt( _header->ddspf.dwFlags );
|
||||
unsigned int ddspf_dwFourCC = LittleInt( _header->ddspf.dwFourCC );
|
||||
|
||||
if ( magic != DDS_MAKEFOURCC('D', 'D', 'S', ' ')) {
|
||||
common->Printf( "CheckPrecompressedImage( %s ): magic != 'DDS '\n", imgName.c_str() );
|
||||
|
@ -1401,11 +1405,27 @@ bool idImage::CheckPrecompressedImage( bool fullLoad ) {
|
|||
|
||||
// if we don't support color index textures, we must load the full image
|
||||
// should we just expand the 256 color image to 32 bit for upload?
|
||||
if ( ddspf_dwFlags & DDSF_ID_INDEXCOLOR && !glConfig.sharedTexturePaletteAvailable ) {
|
||||
if ( (ddspf_dwFlags & DDSF_ID_INDEXCOLOR) && !glConfig.sharedTexturePaletteAvailable ) {
|
||||
R_StaticFree( data );
|
||||
return false;
|
||||
}
|
||||
|
||||
// DG: same if this is a BC7 (BPTC) texture but the GPU doesn't support that
|
||||
// or if it uses the additional DX10 header and is *not* a BC7 texture
|
||||
if ( ddspf_dwFourCC == DDS_MAKEFOURCC( 'D', 'X', '1', '0' ) ) {
|
||||
ddsDXT10addHeader_t *dx10Header = (ddsDXT10addHeader_t *)( data + 4 + sizeof(ddsFileHeader_t) );
|
||||
unsigned int dxgiFormat = LittleInt( dx10Header->dxgiFormat );
|
||||
if ( dxgiFormat != 98 // DXGI_FORMAT_BC7_UNORM
|
||||
|| !glConfig.bptcTextureCompressionAvailable ) {
|
||||
if (dxgiFormat != 98) {
|
||||
common->Warning( "Image file '%s' has unsupported dxgiFormat %d - dhewm3 only supports DXGI_FORMAT_BC7_UNORM (98)!",
|
||||
filename, dxgiFormat);
|
||||
}
|
||||
R_StaticFree( data );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// upload all the levels
|
||||
UploadPrecompressedImage( data, len );
|
||||
|
||||
|
@ -1455,6 +1475,7 @@ void idImage::UploadPrecompressedImage( byte *data, int len ) {
|
|||
|
||||
uploadWidth = header->dwWidth;
|
||||
uploadHeight = header->dwHeight;
|
||||
size_t additionalHeaderOffset = 0; // used if the DDS has a DDS_HEADER_DXT10
|
||||
if ( header->ddspf.dwFlags & DDSF_FOURCC ) {
|
||||
switch ( header->ddspf.dwFourCC ) {
|
||||
case DDS_MAKEFOURCC( 'D', 'X', 'T', '1' ):
|
||||
|
@ -1473,6 +1494,12 @@ void idImage::UploadPrecompressedImage( byte *data, int len ) {
|
|||
case DDS_MAKEFOURCC( 'R', 'X', 'G', 'B' ):
|
||||
internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
break;
|
||||
case DDS_MAKEFOURCC( 'D', 'X', '1', '0' ): // BC7 aka BPTC
|
||||
additionalHeaderOffset = 20;
|
||||
// Note: this is a bit hacky, but in CheckPrecompressedImage() we made sure
|
||||
// that only BC7 UNORM is accepted if the FourCC is 'DX10'
|
||||
internalFormat = GL_COMPRESSED_RGBA_BPTC_UNORM;
|
||||
break;
|
||||
default:
|
||||
common->Warning( "Invalid compressed internal format\n" );
|
||||
return;
|
||||
|
@ -1510,12 +1537,13 @@ void idImage::UploadPrecompressedImage( byte *data, int len ) {
|
|||
|
||||
int uw = uploadWidth;
|
||||
int uh = uploadHeight;
|
||||
int lastUW = uw, lastUH = uh;
|
||||
|
||||
// We may skip some mip maps if we are downsizing
|
||||
int skipMip = 0;
|
||||
GetDownsize( uploadWidth, uploadHeight );
|
||||
|
||||
byte *imagedata = data + sizeof(ddsFileHeader_t) + 4;
|
||||
byte *imagedata = data + sizeof(ddsFileHeader_t) + 4 + additionalHeaderOffset;
|
||||
|
||||
for ( int i = 0 ; i < numMipmaps; i++ ) {
|
||||
int size = 0;
|
||||
|
@ -1535,6 +1563,8 @@ void idImage::UploadPrecompressedImage( byte *data, int len ) {
|
|||
qglTexImage2D( GL_TEXTURE_2D, i - skipMip, internalFormat, uw, uh, 0, externalFormat, GL_UNSIGNED_BYTE, imagedata );
|
||||
}
|
||||
}
|
||||
lastUW = uw;
|
||||
lastUH = uh;
|
||||
|
||||
imagedata += size;
|
||||
uw /= 2;
|
||||
|
@ -1546,6 +1576,19 @@ void idImage::UploadPrecompressedImage( byte *data, int len ) {
|
|||
uh = 1;
|
||||
}
|
||||
}
|
||||
// in case the mipmap chain is incomplete (doesn't go down to 1x1 pixel)
|
||||
// the texture may be shown as black unless GL_TEXTURE_MAX_LEVEL is set accordingly
|
||||
if ( lastUW > 1 || lastUH > 1 ) {
|
||||
numMipmaps -= skipMip;
|
||||
if ( numMipmaps == 1 ) {
|
||||
// if there is only one mipmap, just don't use mipmapping for this texture
|
||||
if ( filter == TF_DEFAULT ) {
|
||||
filter = TF_LINEAR;
|
||||
}
|
||||
} else {
|
||||
qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, numMipmaps - 1 );
|
||||
}
|
||||
}
|
||||
|
||||
SetImageFilterAndRepeat();
|
||||
}
|
||||
|
@ -2140,6 +2183,9 @@ void idImage::Print() const {
|
|||
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
||||
common->Printf( "DXT5 " );
|
||||
break;
|
||||
case GL_COMPRESSED_RGBA_BPTC_UNORM:
|
||||
common->Printf( "BC7 " );
|
||||
break;
|
||||
case GL_RGBA4:
|
||||
common->Printf( "RGBA4 " );
|
||||
break;
|
||||
|
|
|
@ -63,6 +63,7 @@ typedef struct glconfig_s {
|
|||
|
||||
bool multitextureAvailable;
|
||||
bool textureCompressionAvailable;
|
||||
bool bptcTextureCompressionAvailable; // DG: for GL_ARB_texture_compression_bptc (BC7)
|
||||
bool anisotropicAvailable;
|
||||
bool textureLODBiasAvailable;
|
||||
bool textureEnvAddAvailable;
|
||||
|
|
|
@ -431,8 +431,12 @@ static void R_CheckPortableExtensions( void ) {
|
|||
glConfig.textureCompressionAvailable = true;
|
||||
qglCompressedTexImage2DARB = (PFNGLCOMPRESSEDTEXIMAGE2DARBPROC)GLimp_ExtensionPointer( "glCompressedTexImage2DARB" );
|
||||
qglGetCompressedTexImageARB = (PFNGLGETCOMPRESSEDTEXIMAGEARBPROC)GLimp_ExtensionPointer( "glGetCompressedTexImageARB" );
|
||||
if ( R_CheckExtension( "GL_ARB_texture_compression_bptc" ) ) {
|
||||
glConfig.bptcTextureCompressionAvailable = true;
|
||||
}
|
||||
} else {
|
||||
glConfig.textureCompressionAvailable = false;
|
||||
glConfig.bptcTextureCompressionAvailable = false;
|
||||
}
|
||||
|
||||
// GL_EXT_texture_filter_anisotropic
|
||||
|
|
|
@ -110,6 +110,14 @@ extern PFNGLSTENCILOPSEPARATEPROC qglStencilOpSeparate;
|
|||
extern PFNGLCOMPRESSEDTEXIMAGE2DARBPROC qglCompressedTexImage2DARB;
|
||||
extern PFNGLGETCOMPRESSEDTEXIMAGEARBPROC qglGetCompressedTexImageARB;
|
||||
|
||||
// ARB_texture_compression_bptc - uses ARB_texture_compression, just adds new constants
|
||||
// that might be missing in old OpenGL headers
|
||||
#ifndef GL_COMPRESSED_RGBA_BPTC_UNORM_ARB
|
||||
// currently the only one we use, there's also COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB (0x8E8D)
|
||||
// and COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB (0x8E8E) and COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB (0x8E8F)
|
||||
#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C
|
||||
#endif
|
||||
|
||||
// ARB_vertex_program / ARB_fragment_program
|
||||
extern PFNGLVERTEXATTRIBPOINTERARBPROC qglVertexAttribPointerARB;
|
||||
extern PFNGLENABLEVERTEXATTRIBARRAYARBPROC qglEnableVertexAttribArrayARB;
|
||||
|
|
Loading…
Reference in a new issue