Fixes for BC7

This commit is contained in:
Justin Marshall
2026-05-29 20:10:26 -07:00
parent 1bc7df9614
commit 8c147eb9dd
+54 -4
View File
@@ -1164,6 +1164,21 @@ static float R_NormalProjection( const byte *rgba, const idVec3 &axis ) {
( rgba[2] * ( 2.0f / 255.0f ) - 1.0f ) * axis.z;
}
static byte R_NormalFloatToByte( float v ) {
return (byte)idMath::ClampInt( 0, 255, (int)( ( v * 0.5f + 0.5f ) * 255.0f + 0.5f ) );
}
static float R_NormalToSlopeHeight( const idVec3 &normal ) {
const float slope = idMath::ClampFloat( 0.0f, 1.0f, idMath::Sqrt( normal.x * normal.x + normal.y * normal.y ) );
return idMath::ClampFloat( 0.0f, 1.0f, 0.5f + idMath::Pow( slope, 0.65f ) * 0.5f );
}
static byte R_NormalToBakedHeightByte( const idVec3 &normal, float blockAverageHeight ) {
const float localHeight = 0.5f + ( R_NormalToSlopeHeight( normal ) - blockAverageHeight ) * 0.55f;
const float height = idMath::ClampFloat( 0.35f, 0.65f, localHeight );
return (byte)idMath::ClampInt( 1, 254, (int)( height * 255.0f + 0.5f ) );
}
static void R_EncodeBC7Mode6Block( const byte *rgba, int width, int height, int x, int y, bool normalMap, byte *block ) {
memset( block, 0, 16 );
@@ -1176,8 +1191,11 @@ static void R_EncodeBC7Mode6Block( const byte *rgba, int width, int height, int
idVec3 averageNormal( 0.0f, 0.0f, 0.0f );
idVec3 variance( 0.0f, 0.0f, 0.0f );
idVec3 axis( 1.0f, 0.0f, 0.0f );
float averageProjection = 0.0f;
float averageSlopeHeight = 0.5f;
if ( normalMap ) {
averageSlopeHeight = 0.0f;
for ( int by = 0; by < 4; by++ ) {
const int sy = ( y + by < height ) ? y + by : height - 1;
for ( int bx = 0; bx < 4; bx++ ) {
@@ -1186,9 +1204,18 @@ static void R_EncodeBC7Mode6Block( const byte *rgba, int width, int height, int
averageNormal.x += p[0] * ( 2.0f / 255.0f ) - 1.0f;
averageNormal.y += p[1] * ( 2.0f / 255.0f ) - 1.0f;
averageNormal.z += p[2] * ( 2.0f / 255.0f ) - 1.0f;
idVec3 sampleNormal(
p[0] * ( 2.0f / 255.0f ) - 1.0f,
p[1] * ( 2.0f / 255.0f ) - 1.0f,
p[2] * ( 2.0f / 255.0f ) - 1.0f );
if ( !sampleNormal.Normalize() ) {
sampleNormal.Set( 0.0f, 0.0f, 1.0f );
}
averageSlopeHeight += R_NormalToSlopeHeight( sampleNormal );
}
}
averageNormal *= 1.0f / 16.0f;
averageSlopeHeight *= 1.0f / 16.0f;
for ( int by = 0; by < 4; by++ ) {
const int sy = ( y + by < height ) ? y + by : height - 1;
for ( int bx = 0; bx < 4; bx++ ) {
@@ -1207,6 +1234,7 @@ static void R_EncodeBC7Mode6Block( const byte *rgba, int width, int height, int
} else if ( variance.z > variance.x && variance.z > variance.y ) {
axis.Set( 0.0f, 0.0f, 1.0f );
}
averageProjection = averageNormal * axis;
}
for ( int by = 0; by < 4; by++ ) {
@@ -1218,11 +1246,9 @@ static void R_EncodeBC7Mode6Block( const byte *rgba, int width, int height, int
const float projection = R_NormalProjection( p, axis );
if ( projection < minProjection ) {
minProjection = projection;
memcpy( minColor, p, 4 );
}
if ( projection > maxProjection ) {
maxProjection = projection;
memcpy( maxColor, p, 4 );
}
continue;
}
@@ -1238,6 +1264,30 @@ static void R_EncodeBC7Mode6Block( const byte *rgba, int width, int height, int
}
}
if ( normalMap ) {
idVec3 minNormal = averageNormal + axis * ( minProjection - averageProjection );
idVec3 maxNormal = averageNormal + axis * ( maxProjection - averageProjection );
if ( variance.x + variance.y + variance.z < 0.0002f || ( maxProjection - minProjection ) < 0.01f ) {
minNormal = averageNormal;
maxNormal = averageNormal;
minProjection = maxProjection = averageProjection;
}
if ( !minNormal.Normalize() ) {
minNormal.Set( 0.0f, 0.0f, 1.0f );
}
if ( !maxNormal.Normalize() ) {
maxNormal.Set( 0.0f, 0.0f, 1.0f );
}
minColor[0] = R_NormalFloatToByte( minNormal.x );
minColor[1] = R_NormalFloatToByte( minNormal.y );
minColor[2] = R_NormalFloatToByte( minNormal.z );
minColor[3] = R_NormalToBakedHeightByte( minNormal, averageSlopeHeight );
maxColor[0] = R_NormalFloatToByte( maxNormal.x );
maxColor[1] = R_NormalFloatToByte( maxNormal.y );
maxColor[2] = R_NormalFloatToByte( maxNormal.z );
maxColor[3] = R_NormalToBakedHeightByte( maxNormal, averageSlopeHeight );
}
int bit = 0;
R_WriteBitsLSB( block, bit, 0x40, 7 ); // BC7 mode 6: six zero mode bits followed by one.
@@ -1294,7 +1344,7 @@ static void R_AppendBytes( idList<byte> &dst, const void *src, int bytes ) {
}
static unsigned long R_GeneratedBC7CookVersion() {
return DDS_MAKEFOURCC( 'B', '7', 'C', '3' );
return DDS_MAKEFOURCC( 'B', '7', 'C', '8' );
}
static bool R_GetDDSAverageColor( const byte *data, int len, float averageColor[4] ) {
@@ -1456,7 +1506,7 @@ bool idImage::CookGeneratedBC7Image( const byte *pic, int width, int height, ID_
header.dwFlags = DDSF_CAPS | DDSF_PIXELFORMAT | DDSF_WIDTH | DDSF_HEIGHT | DDSF_LINEARSIZE;
header.dwHeight = scaled_height;
header.dwWidth = scaled_width;
header.dwPitchOrLinearSize = ( ( scaled_width + 3 ) / 4 ) * 16;
header.dwPitchOrLinearSize = ( ( scaled_width + 3 ) / 4 ) * ( ( scaled_height + 3 ) / 4 ) * 16;
header.dwMipMapCount = numLevels;
header.dwFlags |= DDSF_MIPMAPCOUNT;
header.dwCaps1 = DDSF_TEXTURE | DDSF_MIPMAP | DDSF_COMPLEX;