Added HDRI extension to the material system

This commit is contained in:
Robert Beckebans 2025-01-03 17:34:22 +01:00
parent 31fdbfed42
commit f5cb915356
6 changed files with 414 additions and 13 deletions

View file

@ -735,6 +735,15 @@ void idBinaryImage::LoadCubeFromMemory( int width, const byte* pics[6], int numL
dxt.CompressImageDXT5Fast( padSrc, img.data, padSize, padSize );
}
}
else if( textureFormat == FMT_R11G11B10F )
{
// RB: copy it as it was a RGBA8 because of the same size
img.Alloc( padSize * padSize * 4 );
for( int i = 0; i < img.dataSize; i++ )
{
img.data[ i ] = pic[ i ];
}
}
else
{
fileData.format = textureFormat = FMT_RGBA8;

View file

@ -674,8 +674,10 @@ void R_ApplyCubeMapTransforms( int i, byte* data, int size );
// SP begin
// This method takes in a cubemap from a single image. Depending on the side (0-5),
// the image will be extracted from data and returned. The dimensions will be size x size.
byte* R_GenerateCubeMapSideFromSingleImage( byte* data, int srcWidth, int srcHeight, int size, int side );
byte* R_GenerateCubeMapSideFromSingleImage( const byte* in, int srcWidth, int srcHeight, int size, int side );
// SP end
// RB
byte* R_GenerateCubeMapSideFromPanoramaImage( const byte* in, int srcWidth, int srcHeight, int size, int side );
idVec4 R_CalculateMipRect( uint dimensions, uint mip );
int R_CalculateUsedAtlasPixels( int dimensions );

View file

@ -905,14 +905,14 @@ static void LoadHDR( const char* filename, unsigned char** pic, int* width, int*
int32 numChannels;
float* rgba = stbi_loadf_from_memory( ( stbi_uc const* ) fbuffer, fileSize, width, height, &numChannels, 0 );
float* rgb = stbi_loadf_from_memory( ( stbi_uc const* ) fbuffer, fileSize, width, height, &numChannels, 0 );
if( numChannels != 3 )
{
common->Error( "LoadHDR( %s ): HDR has not 3 channels\n", filename );
}
if( rgba )
if( rgb )
{
int32 pixelCount = *width * *height;
byte* out = ( byte* )R_StaticAlloc( pixelCount * 4, TAG_IMAGE );
@ -921,7 +921,7 @@ static void LoadHDR( const char* filename, unsigned char** pic, int* width, int*
// convert to packed R11G11B10F as uint32 for each pixel
const float* src = rgba;
const float* src = rgb;
byte* dst = out;
for( int i = 0; i < pixelCount; i++ )
{
@ -936,11 +936,11 @@ static void LoadHDR( const char* filename, unsigned char** pic, int* width, int*
uint32_t value = float3_to_r11g11b10f( p );
*( uint32_t* )dst = value;
src += 4;
src += 3;
dst += 4;
}
free( rgba );
free( rgb );
}
Mem_Free( ( void* )fbuffer );
@ -1268,6 +1268,52 @@ bool R_LoadCubeImages( const char* imgName, cubeFiles_t extensions, byte* pics[6
return true;
}
if( extensions == CF_PANORAMA )
{
ID_TIME_T thisTime;
byte* thisPic[1];
thisPic[0] = nullptr;
if( pics )
{
R_LoadImageProgram( imgName, thisPic, &width, &height, &thisTime );
}
else
{
// load just the timestamps
R_LoadImageProgram( imgName, nullptr, &width, &height, &thisTime );
}
if( thisTime == FILE_NOT_FOUND_TIMESTAMP )
{
return false;
}
if( timestamp )
{
if( thisTime > *timestamp )
{
*timestamp = thisTime;
}
}
if( pics )
{
cubeMapSize = 512;
*outSize = cubeMapSize;
for( int i = 0; i < 6; i++ )
{
pics[i] = R_GenerateCubeMapSideFromPanoramaImage( thisPic[0], width, height, cubeMapSize, i );
}
R_StaticFree( thisPic[0] );
}
return true;
}
for( i = 0 ; i < 6 ; i++ )
{
idStr::snPrintf( fullName, sizeof( fullName ), "%s%s", imgName, sides[i] );

View file

@ -574,7 +574,7 @@ void idImage::ActuallyLoadImage( bool fromBackEnd, nvrhi::ICommandList* commandL
{
opts.textureType = TT_2D_ARRAY;
}
else if( cubeFiles == CF_NATIVE || cubeFiles == CF_CAMERA || cubeFiles == CF_QUAKE1 || cubeFiles == CF_SINGLE )
else if( cubeFiles == CF_NATIVE || cubeFiles == CF_CAMERA || cubeFiles == CF_QUAKE1 || cubeFiles == CF_SINGLE || cubeFiles == CF_PANORAMA )
{
opts.textureType = TT_CUBIC;
repeat = TR_CLAMP;
@ -714,7 +714,7 @@ void idImage::ActuallyLoadImage( bool fromBackEnd, nvrhi::ICommandList* commandL
//else if( toolUsage )
// binarizeReason = va( "binarize: tool usage '%s'", generatedName.c_str() );
if( cubeFiles == CF_NATIVE || cubeFiles == CF_CAMERA || cubeFiles == CF_QUAKE1 || cubeFiles == CF_SINGLE )
if( cubeFiles == CF_NATIVE || cubeFiles == CF_CAMERA || cubeFiles == CF_QUAKE1 || cubeFiles == CF_SINGLE || cubeFiles == CF_PANORAMA )
{
int size;
byte* pics[6];
@ -1242,7 +1242,7 @@ void idImage::Reload( bool force, nvrhi::ICommandList* commandList )
if( !force )
{
ID_TIME_T current;
if( cubeFiles == CF_NATIVE || cubeFiles == CF_CAMERA || cubeFiles == CF_QUAKE1 || cubeFiles == CF_SINGLE )
if( cubeFiles == CF_NATIVE || cubeFiles == CF_CAMERA || cubeFiles == CF_QUAKE1 || cubeFiles == CF_SINGLE || cubeFiles == CF_PANORAMA )
{
R_LoadCubeImages( imgName, cubeFiles, NULL, NULL, &current );
}

View file

@ -631,8 +631,7 @@ int R_CalculateUsedAtlasPixels( int dimensions )
}
// SP begin
byte* R_GenerateCubeMapSideFromSingleImage( byte* data, int srcWidth, int srcHeight, int size, int side )
byte* R_GenerateCubeMapSideFromSingleImage( const byte* in, int srcWidth, int srcHeight, int size, int side )
{
size_t x = 0, y = 0;
switch( side )
@ -689,7 +688,7 @@ byte* R_GenerateCubeMapSideFromSingleImage( byte* data, int srcWidth, int srcHei
const size_t copySize = ( size_t )size * ( size_t )size * 4;
byte* out = ( byte* )R_StaticAlloc( copySize, TAG_IMAGE );
uint32_t* out_p = ( uint32_t* )out;
const uint32_t* in_p = ( uint32_t* )data + x + y * srcWidth;
const uint32_t* in_p = ( uint32_t* )in + x + y * srcWidth;
for( int j = 0; j < size; j++ )
{
@ -701,5 +700,321 @@ byte* R_GenerateCubeMapSideFromSingleImage( byte* data, int srcWidth, int srcHei
return out;
}
// SP end
// RB: ripped from cmft utils by Dario Manesku
/*
* Copyright 2014-2015 Dario Manesku. All rights reserved.
* License: http://www.opensource.org/licenses/BSD-2-Clause
*/
///
///
/// +----------+
/// | +---->+x |
/// | | |
/// | | +y |
/// |+z 2 |
/// +----------+----------+----------+----------+
/// | +---->+z | +---->+x | +---->-z | +---->-x |
/// | | | | | | | | |
/// | | -x | | +z | | +x | | -z |
/// |-y 1 |-y 4 |-y 0 |-y 5 |
/// +----------+----------+----------+----------+
/// | +---->+x |
/// | | |
/// | | -y |
/// |-z 3 |
/// +----------+
///
static const float s_faceUvVectors[6][3][3] =
{
{
// +x face
{ 0.0f, 0.0f, -1.0f }, // u -> -z
{ 0.0f, -1.0f, 0.0f }, // v -> -y
{ 1.0f, 0.0f, 0.0f }, // +x face
},
{
// -x face
{ 0.0f, 0.0f, 1.0f }, // u -> +z
{ 0.0f, -1.0f, 0.0f }, // v -> -y
{ -1.0f, 0.0f, 0.0f }, // -x face
},
{
// +y face
{ 1.0f, 0.0f, 0.0f }, // u -> +x
{ 0.0f, 0.0f, 1.0f }, // v -> +z
{ 0.0f, 1.0f, 0.0f }, // +y face
},
{
// -y face
{ 1.0f, 0.0f, 0.0f }, // u -> +x
{ 0.0f, 0.0f, -1.0f }, // v -> -z
{ 0.0f, -1.0f, 0.0f }, // -y face
},
{
// +z face
{ 1.0f, 0.0f, 0.0f }, // u -> +x
{ 0.0f, -1.0f, 0.0f }, // v -> -y
{ 0.0f, 0.0f, 1.0f }, // +z face
},
{
// -z face
{ -1.0f, 0.0f, 0.0f }, // u -> -x
{ 0.0f, -1.0f, 0.0f }, // v -> -y
{ 0.0f, 0.0f, -1.0f }, // -z face
}
};
enum
{
CMFT_FACE_POS_X = 0,
CMFT_FACE_NEG_X = 1,
CMFT_FACE_POS_Y = 2,
CMFT_FACE_NEG_Y = 3,
CMFT_FACE_POS_Z = 4,
CMFT_FACE_NEG_Z = 5,
};
enum
{
CMFT_EDGE_LEFT = 0,
CMFT_EDGE_RIGHT = 1,
CMFT_EDGE_TOP = 2,
CMFT_EDGE_BOTTOM = 3,
};
///
/// --> U _____
/// | | |
/// v | +Y |
/// V _____|_____|_____ _____
/// | | | | |
/// | -X | +Z | +X | -Z |
/// |_____|_____|_____|_____|
/// | |
/// | -Y |
/// |_____|
///
/// Neighbour faces in order: left, right, top, bottom.
/// FaceEdge is the edge that belongs to the neighbour face.
static const struct CubeFaceNeighbour
{
uint8_t m_faceIdx;
uint8_t m_faceEdge;
} s_cubeFaceNeighbours[6][4] =
{
{
//POS_X
{ CMFT_FACE_POS_Z, CMFT_EDGE_RIGHT },
{ CMFT_FACE_NEG_Z, CMFT_EDGE_LEFT },
{ CMFT_FACE_POS_Y, CMFT_EDGE_RIGHT },
{ CMFT_FACE_NEG_Y, CMFT_EDGE_RIGHT },
},
{
//NEG_X
{ CMFT_FACE_NEG_Z, CMFT_EDGE_RIGHT },
{ CMFT_FACE_POS_Z, CMFT_EDGE_LEFT },
{ CMFT_FACE_POS_Y, CMFT_EDGE_LEFT },
{ CMFT_FACE_NEG_Y, CMFT_EDGE_LEFT },
},
{
//POS_Y
{ CMFT_FACE_NEG_X, CMFT_EDGE_TOP },
{ CMFT_FACE_POS_X, CMFT_EDGE_TOP },
{ CMFT_FACE_NEG_Z, CMFT_EDGE_TOP },
{ CMFT_FACE_POS_Z, CMFT_EDGE_TOP },
},
{
//NEG_Y
{ CMFT_FACE_NEG_X, CMFT_EDGE_BOTTOM },
{ CMFT_FACE_POS_X, CMFT_EDGE_BOTTOM },
{ CMFT_FACE_POS_Z, CMFT_EDGE_BOTTOM },
{ CMFT_FACE_NEG_Z, CMFT_EDGE_BOTTOM },
},
{
//POS_Z
{ CMFT_FACE_NEG_X, CMFT_EDGE_RIGHT },
{ CMFT_FACE_POS_X, CMFT_EDGE_LEFT },
{ CMFT_FACE_POS_Y, CMFT_EDGE_BOTTOM },
{ CMFT_FACE_NEG_Y, CMFT_EDGE_TOP },
},
{
//NEG_Z
{ CMFT_FACE_POS_X, CMFT_EDGE_RIGHT },
{ CMFT_FACE_NEG_X, CMFT_EDGE_LEFT },
{ CMFT_FACE_POS_Y, CMFT_EDGE_TOP },
{ CMFT_FACE_NEG_Y, CMFT_EDGE_BOTTOM },
}
};
/// _u and _v should be center adressing and in [-1.0+invSize..1.0-invSize] range.
static inline void TexelCoordToVec( float* _out3f, float _u, float _v, uint8_t _faceId )
{
// out = u * s_faceUv[0] + v * s_faceUv[1] + s_faceUv[2].
_out3f[0] = s_faceUvVectors[_faceId][0][0] * _u + s_faceUvVectors[_faceId][1][0] * _v + s_faceUvVectors[_faceId][2][0];
_out3f[1] = s_faceUvVectors[_faceId][0][1] * _u + s_faceUvVectors[_faceId][1][1] * _v + s_faceUvVectors[_faceId][2][1];
_out3f[2] = s_faceUvVectors[_faceId][0][2] * _u + s_faceUvVectors[_faceId][1][2] * _v + s_faceUvVectors[_faceId][2][2];
// Normalize.
const float invLen = 1.0f / sqrtf( _out3f[0] * _out3f[0] + _out3f[1] * _out3f[1] + _out3f[2] * _out3f[2] );
_out3f[0] *= invLen;
_out3f[1] *= invLen;
_out3f[2] *= invLen;
}
/// Notice: _faceSize should not be equal to one!
static inline float WarpFixupFactor( float _faceSize )
{
// Edge fixup.
// Based on Nvtt : http://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvtt/CubeSurface.cpp
if( _faceSize == 1.0f )
{
return 1.0f;
}
const float fs = _faceSize;
const float fsmo = fs - 1.0f;
return ( fs * fs ) / ( fsmo * fsmo * fsmo );
}
/// _u and _v should be center adressing and in [-1.0+invSize..1.0-invSize] range.
static inline void TexelCoordToVecWarp( float* _out3f, float _u, float _v, uint8_t _faceId, float _warpFixup )
{
_u = ( _warpFixup * _u * _u * _u ) + _u;
_v = ( _warpFixup * _v * _v * _v ) + _v;
TexelCoordToVec( _out3f, _u, _v, _faceId );
}
inline void vec3Mul( float* __restrict _result, const float* __restrict _a, float _b )
{
_result[0] = _a[0] * _b;
_result[1] = _a[1] * _b;
_result[2] = _a[2] * _b;
}
inline float vec3Dot( const float* __restrict _a, const float* __restrict _b )
{
return _a[0] * _b[0] + _a[1] * _b[1] + _a[2] * _b[2];
}
/// _u and _v are in [0.0 .. 1.0] range.
static inline void VecToTexelCoord( float& _u, float& _v, uint8_t& _faceIdx, const float* _vec )
{
const float absVec[3] =
{
fabsf( _vec[0] ),
fabsf( _vec[1] ),
fabsf( _vec[2] ),
};
const float max = fmaxf( fmaxf( absVec[0], absVec[1] ), absVec[2] );
// Get face id (max component == face vector).
if( max == absVec[0] )
{
_faceIdx = ( _vec[0] >= 0.0f ) ? uint8_t( CMFT_FACE_POS_X ) : uint8_t( CMFT_FACE_NEG_X );
}
else if( max == absVec[1] )
{
_faceIdx = ( _vec[1] >= 0.0f ) ? uint8_t( CMFT_FACE_POS_Y ) : uint8_t( CMFT_FACE_NEG_Y );
}
else //if (max == absVec[2])
{
_faceIdx = ( _vec[2] >= 0.0f ) ? uint8_t( CMFT_FACE_POS_Z ) : uint8_t( CMFT_FACE_NEG_Z );
}
// Divide by max component.
float faceVec[3];
vec3Mul( faceVec, _vec, 1.0f / max );
// Project other two components to face uv basis.
_u = ( vec3Dot( s_faceUvVectors[_faceIdx][0], faceVec ) + 1.0f ) * 0.5f;
_v = ( vec3Dot( s_faceUvVectors[_faceIdx][1], faceVec ) + 1.0f ) * 0.5f;
}
#define CMFT_RPI 0.31830988618379067153f
static inline void LatLongFromVec( float& _u, float& _v, const float _vec[3] )
{
const float phi = atan2f( _vec[0], _vec[2] );
const float theta = acosf( _vec[1] );
_u = ( idMath::PI + phi ) * ( 0.5f / idMath::PI );
_v = theta * CMFT_RPI;
}
static inline void VecFromLatLong( float _vec[3], float _u, float _v )
{
const float phi = _u * idMath::TWO_PI;
const float theta = _v * idMath::PI;
_vec[0] = -sinf( theta ) * sinf( phi );
_vec[1] = cosf( theta );
_vec[2] = -sinf( theta ) * cosf( phi );
}
byte* R_GenerateCubeMapSideFromPanoramaImage( const byte* in, int srcWidth, int srcHeight, int cubeWidth, int side )
{
size_t x = 0, y = 0;
const uint32 bytesPerPixel = 4;
const int copySize = cubeWidth * cubeWidth * bytesPerPixel;
byte* out = ( byte* )R_StaticAlloc( copySize, TAG_IMAGE );
uint32_t* out_p = ( uint32_t* )out;
const uint32 dstFaceSize = cubeWidth;
const uint32 dstPitch = dstFaceSize * bytesPerPixel;
//const uint32 dstFaceDataSize = dstPitch * dstFaceSize;
//const uint32 dstDataSize = dstFaceDataSize * 6;
const float srcWidthMinusOne = float( int32( srcWidth - 1 ) );
const float srcHeightMinusOne = float( int32( srcHeight - 1 ) );
const uint32 srcPitch = srcWidth * bytesPerPixel;
const float invDstFaceSizef = 1.0f / float( dstFaceSize );
// maybe wrong, done by observation
const idMat4 toDoomTransform( idAngles( -90, 0, -90 ).ToMat3(), vec3_origin );
byte* dstFaceData = ( byte* )out;// + face * dstFaceDataSize;
for( int y = 0; y < dstFaceSize; y++ )
{
byte* dstRowData = ( byte* )dstFaceData + y * dstPitch;
for( int x = 0; x < dstFaceSize; x++ )
{
// Cubemap (u,v) on current face.
const float uu = 2.0f * x * invDstFaceSizef - 1.0f;
const float vv = 2.0f * y * invDstFaceSizef - 1.0f;
// Get cubemap vector (x,y,z) from (u,v,faceIdx).
idVec3 vec;
TexelCoordToVec( &vec[0], uu, vv, side );
vec = toDoomTransform * vec;
// Convert cubemap vector (x,y,z) to latlong (u,v).
float xSrcf;
float ySrcf;
LatLongFromVec( xSrcf, ySrcf, &vec[0] );
// Convert from [0..1] to [0..(size-1)] range.
xSrcf *= srcWidthMinusOne;
ySrcf *= srcHeightMinusOne;
const int32 xSrc = int32( idMath::Fabs( idMath::Rint( xSrcf ) ) );
const int32 ySrc = int32( idMath::Fabs( idMath::Rint( ySrcf ) ) );
const byte* src = ( const byte* )in + ySrc * srcPitch + xSrc * bytesPerPixel ;
byte* dstColumnData = ( uint8_t* )dstRowData + x * bytesPerPixel;
dstColumnData[0] = src[0];
dstColumnData[1] = src[1];
dstColumnData[2] = src[2];
dstColumnData[3] = src[3];
}
}
return out;
}
// RB end

View file

@ -1285,6 +1285,17 @@ void idMaterial::ParseFragmentMap( idLexer& src, newShaderStage_t* newStage )
cubeMap = CF_SINGLE;
continue;
}
if( !token.Icmp( "panoramaMap" ) )
{
cubeMap = CF_PANORAMA;
continue;
}
if( !token.Icmp( "hdriMap" ) )
{
cubeMap = CF_PANORAMA;
td = TD_R11G11B10F;
continue;
}
if( !token.Icmp( "nearest" ) )
{
tf = TF_NEAREST;
@ -1807,6 +1818,24 @@ void idMaterial::ParseStage( idLexer& src, const textureRepeat_t trpDefault )
continue;
}
if( !token.Icmp( "panoramaMap" ) )
{
str = R_ParsePastImageProgram( src );
idStr::Copynz( imageName, str, sizeof( imageName ) );
cubeMap = CF_PANORAMA;
td = TD_HIGHQUALITY_CUBE;
continue;
}
if( !token.Icmp( "hdriMap" ) )
{
str = R_ParsePastImageProgram( src );
idStr::Copynz( imageName, str, sizeof( imageName ) );
cubeMap = CF_PANORAMA;
td = TD_R11G11B10F;
continue;
}
if( !token.Icmp( "cubeMapSize" ) )
{
cubeMapSize = src.ParseInt();