2022-09-18 15:37:21 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Copyright ( C ) 1999 - 2005 , Id Software , Inc .
Copyright ( C ) 2000 - 2013 , Raven Software , Inc .
Copyright ( C ) 2001 - 2013 , Activision , Inc .
Copyright ( C ) 2005 - 2015 , ioquake3 contributors
Copyright ( C ) 2013 - 2015 , OpenJK contributors
This file is part of the OpenJK source code .
OpenJK is free software ; you can redistribute it and / or modify it
under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , see < http : //www.gnu.org/licenses/>.
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
// tr_image.c
# include "tr_local.h"
# include "../rd-common/tr_common.h"
# include <png.h>
# include <map>
2022-11-23 22:17:58 +00:00
using namespace std ;
2022-09-18 15:37:21 +00:00
2022-09-20 21:08:09 +00:00
# if !defined(GL_RGB5)
# define GL_RGB5 0x8050 // same as GL_RGB5_EXT
# endif
# if !defined(GL_RGBA4)
# define GL_RGBA4 0x8056 // same as GL_RGBA4_EXT and GL_RGBA4_OES
# endif
2022-09-18 15:37:21 +00:00
static byte s_intensitytable [ 256 ] ;
static unsigned char s_gammatable [ 256 ] ;
int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST ;
int gl_filter_max = GL_LINEAR ;
# define FILE_HASH_SIZE 1024 // actually, the shader code needs this (from another module, great).
//static image_t* hashTable[FILE_HASH_SIZE];
/*
* * R_GammaCorrect
*/
void R_GammaCorrect ( byte * buffer , int bufSize ) {
int i ;
for ( i = 0 ; i < bufSize ; i + + ) {
buffer [ i ] = s_gammatable [ buffer [ i ] ] ;
}
}
2022-11-23 22:17:58 +00:00
typedef struct textureMode_s {
2022-09-18 15:37:21 +00:00
const char * name ;
int minimize , maximize ;
} textureMode_t ;
textureMode_t modes [ ] = {
2022-11-23 22:17:58 +00:00
{ " GL_NEAREST " , GL_NEAREST , GL_NEAREST } ,
{ " GL_LINEAR " , GL_LINEAR , GL_LINEAR } ,
{ " GL_NEAREST_MIPMAP_NEAREST " , GL_NEAREST_MIPMAP_NEAREST , GL_NEAREST } ,
{ " GL_LINEAR_MIPMAP_NEAREST " , GL_LINEAR_MIPMAP_NEAREST , GL_LINEAR } ,
{ " GL_NEAREST_MIPMAP_LINEAR " , GL_NEAREST_MIPMAP_LINEAR , GL_NEAREST } ,
{ " GL_LINEAR_MIPMAP_LINEAR " , GL_LINEAR_MIPMAP_LINEAR , GL_LINEAR }
2022-09-18 15:37:21 +00:00
} ;
static const size_t numTextureModes = ARRAY_LEN ( modes ) ;
/*
= = = = = = = = = = = = = = = =
return a hash value for the filename
= = = = = = = = = = = = = = = =
*/
long generateHashValue ( const char * fname ) {
int i ;
long hash ;
char letter ;
hash = 0 ;
i = 0 ;
while ( fname [ i ] ! = ' \0 ' ) {
letter = tolower ( fname [ i ] ) ;
if ( letter = = ' . ' ) break ; // don't include extension
if ( letter = = ' \\ ' ) letter = ' / ' ; // damn path names
hash + = ( long ) ( letter ) * ( i + 119 ) ;
i + + ;
}
hash & = ( FILE_HASH_SIZE - 1 ) ;
return hash ;
}
// makeup a nice clean, consistant name to query for and file under, for map<> usage...
//
char * GenerateImageMappingName ( const char * name )
{
static char sName [ MAX_QPATH ] ;
int i = 0 ;
char letter ;
while ( name [ i ] ! = ' \0 ' & & i < MAX_QPATH - 1 )
{
letter = tolower ( name [ i ] ) ;
if ( letter = = ' . ' ) break ; // don't include extension
if ( letter = = ' \\ ' ) letter = ' / ' ; // damn path names
sName [ i + + ] = letter ;
}
sName [ i ] = 0 ;
return & sName [ 0 ] ;
}
/*
= = = = = = = = = = = = = = =
GL_TextureMode
= = = = = = = = = = = = = = =
*/
void GL_TextureMode ( const char * string ) {
size_t i ;
image_t * glt ;
for ( i = 0 ; i < numTextureModes ; i + + ) {
if ( ! Q_stricmp ( modes [ i ] . name , string ) ) {
break ;
}
}
if ( i = = numTextureModes ) {
2022-09-19 22:22:36 +00:00
ri . Printf ( PRINT_ALL , " bad filter name \n " ) ;
2022-09-18 15:37:21 +00:00
for ( i = 0 ; i < numTextureModes ; i + + ) {
2022-09-19 22:22:36 +00:00
ri . Printf ( PRINT_ALL , " %s \n " , modes [ i ] . name ) ;
2022-09-18 15:37:21 +00:00
}
return ;
}
gl_filter_min = modes [ i ] . minimize ;
gl_filter_max = modes [ i ] . maximize ;
// If the level they requested is less than possible, set the max possible...
if ( r_ext_texture_filter_anisotropic - > value > glConfig . maxTextureFilterAnisotropy )
2022-09-19 22:22:36 +00:00
ri . Cvar_SetValue ( " r_ext_texture_filter_anisotropic " , glConfig . maxTextureFilterAnisotropy ) ;
2022-09-18 15:37:21 +00:00
// change all the existing mipmap texture objects
2022-09-19 21:46:47 +00:00
R_Images_StartIteration ( ) ;
2022-09-18 15:37:21 +00:00
while ( ( glt = R_Images_GetNextIteration ( ) ) ! = NULL )
{
if ( glt - > mipmap ) {
GL_Bind ( glt ) ;
qglTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , gl_filter_min ) ;
qglTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , gl_filter_max ) ;
if ( glConfig . maxTextureFilterAnisotropy > 0 ) {
if ( r_ext_texture_filter_anisotropic - > integer > 1 ) {
qglTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_MAX_ANISOTROPY_EXT , r_ext_texture_filter_anisotropic - > value ) ;
} else {
qglTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_MAX_ANISOTROPY_EXT , 1.0f ) ;
}
}
}
}
}
2022-11-23 22:17:58 +00:00
2022-09-18 15:37:21 +00:00
static float R_BytesPerTex ( int format )
{
switch ( format ) {
2022-09-19 21:46:47 +00:00
case 1 :
//"I "
return 1 ;
break ;
case 2 :
//"IA "
return 2 ;
break ;
case 3 :
//"RGB "
# ifdef HAVE_GLES
return 3 ;
# else
return glConfig . colorBits / 8.0f ;
# endif
break ;
case 4 :
//"RGBA "
# ifdef HAVE_GLES
return 4 ;
# else
return glConfig . colorBits / 8.0f ;
break ;
case GL_RGBA4 :
//"RGBA4"
return 2 ;
break ;
case GL_RGB5 :
//"RGB5 "
return 2 ;
2022-11-23 22:17:58 +00:00
# endif
2022-09-19 21:46:47 +00:00
break ;
2022-11-23 22:17:58 +00:00
# ifndef HAVE_GLES
case GL_RGBA8 :
//"RGBA8"
return 4 ;
break ;
case GL_RGB8 :
//"RGB8"
return 4 ;
break ;
case GL_RGB4_S3TC :
//"S3TC "
return 0.33333f ;
break ;
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT :
//"DXT1 "
return 0.33333f ;
break ;
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT :
//"DXT5 "
return 1 ;
break ;
2022-09-19 21:46:47 +00:00
# endif
default :
//"???? "
return 4 ;
2022-09-18 15:37:21 +00:00
}
}
/*
= = = = = = = = = = = = = = =
R_SumOfUsedImages
= = = = = = = = = = = = = = =
*/
float R_SumOfUsedImages ( qboolean bUseFormat )
{
int total = 0 ;
image_t * pImage ;
2022-11-23 22:17:58 +00:00
R_Images_StartIteration ( ) ;
2022-09-18 15:37:21 +00:00
while ( ( pImage = R_Images_GetNextIteration ( ) ) ! = NULL )
{
if ( pImage - > frameUsed = = tr . frameCount - 1 ) { //it has already been advanced for the next frame, so...
if ( bUseFormat )
{
float bytePerTex = R_BytesPerTex ( pImage - > internalFormat ) ;
total + = bytePerTex * ( pImage - > width * pImage - > height ) ;
}
else
{
total + = pImage - > width * pImage - > height ;
}
}
}
return total ;
}
/*
= = = = = = = = = = = = = = =
R_ImageList_f
= = = = = = = = = = = = = = =
*/
void R_ImageList_f ( void ) {
int i = 0 ;
image_t * image ;
2022-11-23 22:17:58 +00:00
int texels = 0 ;
2022-09-18 15:37:21 +00:00
float texBytes = 0.0f ;
const char * yesno [ ] = { " no " , " yes " } ;
2022-11-23 22:17:58 +00:00
ri . Printf ( PRINT_ALL , " \n -w-- -h-- -mm- -if-- wrap --name------- \n " ) ;
2022-09-18 15:37:21 +00:00
int iNumImages = R_Images_StartIteration ( ) ;
while ( ( image = R_Images_GetNextIteration ( ) ) ! = NULL )
{
texels + = image - > width * image - > height ;
texBytes + = image - > width * image - > height * R_BytesPerTex ( image - > internalFormat ) ;
2022-11-23 22:17:58 +00:00
ri . Printf ( PRINT_ALL , " %4i: %4i %4i %s " ,
i , image - > width , image - > height , yesno [ image - > mipmap ] ) ;
2022-09-18 15:37:21 +00:00
switch ( image - > internalFormat ) {
2022-11-23 22:17:58 +00:00
case 1 :
ri . Printf ( PRINT_ALL , " I " ) ;
break ;
case 2 :
ri . Printf ( PRINT_ALL , " IA " ) ;
break ;
case 3 :
ri . Printf ( PRINT_ALL , " RGB " ) ;
break ;
case 4 :
ri . Printf ( PRINT_ALL , " RGBA " ) ;
break ;
2022-09-19 21:46:47 +00:00
# ifndef HAVE_GLES
2022-11-23 22:17:58 +00:00
case GL_RGBA8 :
2022-09-18 15:37:21 +00:00
ri . Printf ( PRINT_ALL , " RGBA8 " ) ;
break ;
case GL_RGB8 :
2022-11-23 22:17:58 +00:00
ri . Printf ( PRINT_ALL , " RGB8 " ) ;
2022-09-18 15:37:21 +00:00
break ;
case GL_RGB4_S3TC :
ri . Printf ( PRINT_ALL , " S3TC " ) ;
break ;
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT :
ri . Printf ( PRINT_ALL , " DXT1 " ) ;
break ;
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT :
ri . Printf ( PRINT_ALL , " DXT5 " ) ;
break ;
case GL_RGBA4 :
ri . Printf ( PRINT_ALL , " RGBA4 " ) ;
break ;
case GL_RGB5 :
ri . Printf ( PRINT_ALL , " RGB5 " ) ;
break ;
2022-11-23 22:17:58 +00:00
2022-09-19 21:46:47 +00:00
# endif
2022-11-23 22:17:58 +00:00
default :
ri . Printf ( PRINT_ALL , " ???? " ) ;
2022-09-18 15:37:21 +00:00
}
switch ( image - > wrapClampMode ) {
2022-11-23 22:17:58 +00:00
case GL_REPEAT :
ri . Printf ( PRINT_ALL , " rept " ) ;
break ;
# ifndef HAVE_GLES
case GL_CLAMP :
2022-09-18 15:37:21 +00:00
ri . Printf ( PRINT_ALL , " clmp " ) ;
break ;
2022-11-23 22:17:58 +00:00
# endif
case GL_CLAMP_TO_EDGE :
ri . Printf ( PRINT_ALL , " clpE " ) ;
break ;
default :
ri . Printf ( PRINT_ALL , " %4i " , image - > wrapClampMode ) ;
break ;
2022-09-18 15:37:21 +00:00
}
ri . Printf ( PRINT_ALL , " %s \n " , image - > imgName ) ;
i + + ;
}
2022-11-23 22:17:58 +00:00
ri . Printf ( PRINT_ALL , " --------- \n " ) ;
ri . Printf ( PRINT_ALL , " -w-- -h-- -mm- -if- wrap --name------- \n " ) ;
ri . Printf ( PRINT_ALL , " %i total texels (not including mipmaps) \n " , texels ) ;
ri . Printf ( PRINT_ALL , " %.2fMB total texture mem (not including mipmaps) \n " , texBytes / 1048576.0f ) ;
ri . Printf ( PRINT_ALL , " %i total images \n \n " , iNumImages ) ;
2022-09-18 15:37:21 +00:00
}
//=======================================================================
/*
= = = = = = = = = = = = = = = =
R_LightScaleTexture
Scale up the pixel values in a texture to increase the
lighting range
= = = = = = = = = = = = = = = =
*/
2022-11-23 22:17:58 +00:00
void R_LightScaleTexture ( unsigned * in , int inwidth , int inheight , qboolean only_gamma )
2022-09-18 15:37:21 +00:00
{
if ( only_gamma )
{
if ( ! glConfig . deviceSupportsGamma )
{
int i , c ;
byte * p ;
p = ( byte * ) in ;
c = inwidth * inheight ;
for ( i = 0 ; i < c ; i + + , p + = 4 )
{
p [ 0 ] = s_gammatable [ p [ 0 ] ] ;
p [ 1 ] = s_gammatable [ p [ 1 ] ] ;
p [ 2 ] = s_gammatable [ p [ 2 ] ] ;
}
}
}
else
{
int i , c ;
byte * p ;
p = ( byte * ) in ;
c = inwidth * inheight ;
if ( glConfig . deviceSupportsGamma )
{
for ( i = 0 ; i < c ; i + + , p + = 4 )
{
p [ 0 ] = s_intensitytable [ p [ 0 ] ] ;
p [ 1 ] = s_intensitytable [ p [ 1 ] ] ;
p [ 2 ] = s_intensitytable [ p [ 2 ] ] ;
}
}
else
{
for ( i = 0 ; i < c ; i + + , p + = 4 )
{
p [ 0 ] = s_gammatable [ s_intensitytable [ p [ 0 ] ] ] ;
p [ 1 ] = s_gammatable [ s_intensitytable [ p [ 1 ] ] ] ;
p [ 2 ] = s_gammatable [ s_intensitytable [ p [ 2 ] ] ] ;
}
}
}
}
/*
= = = = = = = = = = = = = = = =
R_MipMap2
2022-11-23 22:17:58 +00:00
Operates in place , quartering the size of the texture
2022-09-18 15:37:21 +00:00
Proper linear filter
= = = = = = = = = = = = = = = =
*/
static void R_MipMap2 ( unsigned * in , int inWidth , int inHeight ) {
int i , j , k ;
byte * outpix ;
int inWidthMask , inHeightMask ;
int total ;
int outWidth , outHeight ;
unsigned * temp ;
outWidth = inWidth > > 1 ;
outHeight = inHeight > > 1 ;
2022-11-23 22:17:58 +00:00
temp = ( unsigned int * ) R_Malloc ( outWidth * outHeight * 4 , TAG_TEMP_WORKSPACE , qfalse ) ;
2022-09-18 15:37:21 +00:00
inWidthMask = inWidth - 1 ;
inHeightMask = inHeight - 1 ;
for ( i = 0 ; i < outHeight ; i + + ) {
for ( j = 0 ; j < outWidth ; j + + ) {
outpix = ( byte * ) ( temp + i * outWidth + j ) ;
for ( k = 0 ; k < 4 ; k + + ) {
total =
2022-11-23 22:17:58 +00:00
1 * ( ( byte * ) & in [ ( ( i * 2 - 1 ) & inHeightMask ) * inWidth + ( ( j * 2 - 1 ) & inWidthMask ) ] ) [ k ] +
2 * ( ( byte * ) & in [ ( ( i * 2 - 1 ) & inHeightMask ) * inWidth + ( ( j * 2 ) & inWidthMask ) ] ) [ k ] +
2 * ( ( byte * ) & in [ ( ( i * 2 - 1 ) & inHeightMask ) * inWidth + ( ( j * 2 + 1 ) & inWidthMask ) ] ) [ k ] +
1 * ( ( byte * ) & in [ ( ( i * 2 - 1 ) & inHeightMask ) * inWidth + ( ( j * 2 + 2 ) & inWidthMask ) ] ) [ k ] +
2 * ( ( byte * ) & in [ ( ( i * 2 ) & inHeightMask ) * inWidth + ( ( j * 2 - 1 ) & inWidthMask ) ] ) [ k ] +
4 * ( ( byte * ) & in [ ( ( i * 2 ) & inHeightMask ) * inWidth + ( ( j * 2 ) & inWidthMask ) ] ) [ k ] +
4 * ( ( byte * ) & in [ ( ( i * 2 ) & inHeightMask ) * inWidth + ( ( j * 2 + 1 ) & inWidthMask ) ] ) [ k ] +
2 * ( ( byte * ) & in [ ( ( i * 2 ) & inHeightMask ) * inWidth + ( ( j * 2 + 2 ) & inWidthMask ) ] ) [ k ] +
2 * ( ( byte * ) & in [ ( ( i * 2 + 1 ) & inHeightMask ) * inWidth + ( ( j * 2 - 1 ) & inWidthMask ) ] ) [ k ] +
4 * ( ( byte * ) & in [ ( ( i * 2 + 1 ) & inHeightMask ) * inWidth + ( ( j * 2 ) & inWidthMask ) ] ) [ k ] +
4 * ( ( byte * ) & in [ ( ( i * 2 + 1 ) & inHeightMask ) * inWidth + ( ( j * 2 + 1 ) & inWidthMask ) ] ) [ k ] +
2 * ( ( byte * ) & in [ ( ( i * 2 + 1 ) & inHeightMask ) * inWidth + ( ( j * 2 + 2 ) & inWidthMask ) ] ) [ k ] +
1 * ( ( byte * ) & in [ ( ( i * 2 + 2 ) & inHeightMask ) * inWidth + ( ( j * 2 - 1 ) & inWidthMask ) ] ) [ k ] +
2 * ( ( byte * ) & in [ ( ( i * 2 + 2 ) & inHeightMask ) * inWidth + ( ( j * 2 ) & inWidthMask ) ] ) [ k ] +
2 * ( ( byte * ) & in [ ( ( i * 2 + 2 ) & inHeightMask ) * inWidth + ( ( j * 2 + 1 ) & inWidthMask ) ] ) [ k ] +
1 * ( ( byte * ) & in [ ( ( i * 2 + 2 ) & inHeightMask ) * inWidth + ( ( j * 2 + 2 ) & inWidthMask ) ] ) [ k ] ;
2022-09-18 15:37:21 +00:00
outpix [ k ] = total / 36 ;
}
}
}
memcpy ( in , temp , outWidth * outHeight * 4 ) ;
R_Free ( temp ) ;
}
/*
= = = = = = = = = = = = = = = =
R_MipMap
Operates in place , quartering the size of the texture
= = = = = = = = = = = = = = = =
*/
static void R_MipMap ( byte * in , int width , int height ) {
int i , j ;
byte * out ;
int row ;
2022-11-23 22:17:58 +00:00
if ( ! r_simpleMipMaps - > integer ) {
R_MipMap2 ( ( unsigned * ) in , width , height ) ;
2022-09-18 15:37:21 +00:00
return ;
}
2022-11-23 22:17:58 +00:00
if ( width = = 1 & & height = = 1 ) {
2022-09-18 15:37:21 +00:00
return ;
}
row = width * 4 ;
out = in ;
width > > = 1 ;
height > > = 1 ;
if ( width = = 0 | | height = = 0 ) {
width + = height ; // get largest
for ( i = 0 ; i < width ; i + + , out + = 4 , in + = 8 ) {
out [ 0 ] = ( in [ 0 ] + in [ 4 ] ) > > 1 ;
out [ 1 ] = ( in [ 1 ] + in [ 5 ] ) > > 1 ;
out [ 2 ] = ( in [ 2 ] + in [ 6 ] ) > > 1 ;
out [ 3 ] = ( in [ 3 ] + in [ 7 ] ) > > 1 ;
}
return ;
}
for ( i = 0 ; i < height ; i + + , in + = row ) {
for ( j = 0 ; j < width ; j + + , out + = 4 , in + = 8 ) {
out [ 0 ] = ( in [ 0 ] + in [ 4 ] + in [ row + 0 ] + in [ row + 4 ] ) > > 2 ;
out [ 1 ] = ( in [ 1 ] + in [ 5 ] + in [ row + 1 ] + in [ row + 5 ] ) > > 2 ;
out [ 2 ] = ( in [ 2 ] + in [ 6 ] + in [ row + 2 ] + in [ row + 6 ] ) > > 2 ;
out [ 3 ] = ( in [ 3 ] + in [ 7 ] + in [ row + 3 ] + in [ row + 7 ] ) > > 2 ;
}
}
}
/*
= = = = = = = = = = = = = = = = = =
R_BlendOverTexture
Apply a color blend over a set of pixels
= = = = = = = = = = = = = = = = = =
*/
static void R_BlendOverTexture ( byte * data , int pixelCount , byte blend [ 4 ] ) {
int i ;
int inverseAlpha ;
int premult [ 3 ] ;
inverseAlpha = 255 - blend [ 3 ] ;
premult [ 0 ] = blend [ 0 ] * blend [ 3 ] ;
premult [ 1 ] = blend [ 1 ] * blend [ 3 ] ;
premult [ 2 ] = blend [ 2 ] * blend [ 3 ] ;
for ( i = 0 ; i < pixelCount ; i + + , data + = 4 ) {
data [ 0 ] = ( data [ 0 ] * inverseAlpha + premult [ 0 ] ) > > 9 ;
data [ 1 ] = ( data [ 1 ] * inverseAlpha + premult [ 1 ] ) > > 9 ;
data [ 2 ] = ( data [ 2 ] * inverseAlpha + premult [ 2 ] ) > > 9 ;
}
}
byte mipBlendColors [ 16 ] [ 4 ] = {
2022-11-23 22:17:58 +00:00
{ 0 , 0 , 0 , 0 } ,
{ 255 , 0 , 0 , 128 } ,
{ 0 , 255 , 0 , 128 } ,
{ 0 , 0 , 255 , 128 } ,
{ 255 , 0 , 0 , 128 } ,
{ 0 , 255 , 0 , 128 } ,
{ 0 , 0 , 255 , 128 } ,
{ 255 , 0 , 0 , 128 } ,
{ 0 , 255 , 0 , 128 } ,
{ 0 , 0 , 255 , 128 } ,
{ 255 , 0 , 0 , 128 } ,
{ 0 , 255 , 0 , 128 } ,
{ 0 , 0 , 255 , 128 } ,
{ 255 , 0 , 0 , 128 } ,
{ 0 , 255 , 0 , 128 } ,
{ 0 , 0 , 255 , 128 } ,
2022-09-18 15:37:21 +00:00
} ;
2022-11-23 22:17:58 +00:00
# ifdef HAVE_GLES
// helper function for GLES format conversions
byte * gles_convertRGB ( byte * data , int width , int height )
{
byte * temp = ( byte * ) R_Malloc ( width * height * 3 , TAG_TEMP_WORKSPACE , qfalse ) ;
byte * src = data ;
byte * dst = temp ;
for ( int i = 0 ; i < width * height ; i + + ) {
for ( int j = 0 ; j < 3 ; j + + )
* ( dst + + ) = * ( src + + ) ;
src + + ;
}
return temp ;
}
byte * gles_convertRGBA4 ( byte * data , int width , int height )
{
byte * temp = ( byte * ) R_Malloc ( width * height * 2 , TAG_TEMP_WORKSPACE , qfalse ) ;
unsigned int * input = ( unsigned int * ) ( data ) ;
unsigned short * output = ( unsigned short * ) ( temp ) ;
for ( int i = 0 ; i < width * height ; i + + ) {
unsigned int pixel = input [ i ] ;
// Unpack the source data as 8 bit values
unsigned int r = pixel & 0xff ;
unsigned int g = ( pixel > > 8 ) & 0xff ;
unsigned int b = ( pixel > > 16 ) & 0xff ;
unsigned int a = ( pixel > > 24 ) & 0xff ;
// Convert to 4 bit vales
r > > = 4 ; g > > = 4 ; b > > = 4 ; a > > = 4 ;
output [ i ] = r < < 12 | g < < 8 | b < < 4 | a ;
}
return temp ;
}
byte * gles_convertRGB5 ( byte * data , int width , int height )
{
byte * temp = ( byte * ) R_Malloc ( width * height * 2 , TAG_TEMP_WORKSPACE , qfalse ) ;
byte * src = data ;
byte * dst = temp ;
byte r , g , b ;
unsigned int * input = ( unsigned int * ) ( data ) ;
unsigned short * output = ( unsigned short * ) ( temp ) ;
for ( int i = 0 ; i < width * height ; i + + ) {
unsigned int pixel = input [ i ] ;
// Unpack the source data as 8 bit values
unsigned int r = pixel & 0xff ;
unsigned int g = ( pixel > > 8 ) & 0xff ;
unsigned int b = ( pixel > > 16 ) & 0xff ;
// Convert to 4 bit vales
r > > = 3 ; g > > = 2 ; b > > = 3 ;
output [ i ] = r < < 11 | g < < 5 | b ;
}
return temp ;
}
byte * gles_convertLuminance ( byte * data , int width , int height )
{
byte * temp = ( byte * ) R_Malloc ( width * height , TAG_TEMP_WORKSPACE , qfalse ) ;
byte * src = data ;
byte * dst = temp ;
byte r , g , b ;
int i ;
unsigned int * input = ( unsigned int * ) ( data ) ;
byte * output = ( byte * ) ( temp ) ;
for ( i = 0 ; i < width * height ; i + + ) {
unsigned int pixel = input [ i ] ;
// Unpack the source data as 8 bit values
unsigned int r = pixel & 0xff ;
output [ i ] = r ;
}
return temp ;
}
byte * gles_convertLuminanceAlpha ( byte * data , int width , int height )
{
byte * temp = ( byte * ) R_Malloc ( width * height * 2 , TAG_TEMP_WORKSPACE , qfalse ) ;
byte * src = data ;
byte * dst = temp ;
byte r , g , b ;
int i ;
unsigned int * input = ( unsigned int * ) ( data ) ;
unsigned short * output = ( unsigned short * ) ( temp ) ;
for ( i = 0 ; i < width * height ; i + + ) {
unsigned int pixel = input [ i ] ;
// Unpack the source data as 8 bit values
unsigned int r = pixel & 0xff ;
unsigned int a = ( pixel > > 24 ) & 0xff ;
output [ i ] = r | a < < 8 ;
}
return temp ;
}
# endif
class CStringComparator
{
public :
bool operator ( ) ( const char * s1 , const char * s2 ) const { return ( strcmp ( s1 , s2 ) < 0 ) ; }
} ;
typedef map < const char * , image_t * , CStringComparator > AllocatedImages_t ;
AllocatedImages_t AllocatedImages ;
AllocatedImages_t : : iterator itAllocatedImages ;
int giTextureBindNum = 1024 ; // will be set to this anyway at runtime, but wtf?
// return = number of images in the list, for those interested
//
int R_Images_StartIteration ( void )
{
itAllocatedImages = AllocatedImages . begin ( ) ;
return AllocatedImages . size ( ) ;
}
image_t * R_Images_GetNextIteration ( void )
{
if ( itAllocatedImages = = AllocatedImages . end ( ) )
return NULL ;
image_t * pImage = ( * itAllocatedImages ) . second ;
+ + itAllocatedImages ;
return pImage ;
}
// clean up anything to do with an image_t struct, but caller will have to clear the internal to an image_t struct ready for either struct free() or overwrite...
//
// (avoid using ri.xxxx stuff here in case running on dedicated)
//
static void R_Images_DeleteImageContents ( image_t * pImage )
{
assert ( pImage ) ; // should never be called with NULL
if ( pImage )
{
qglDeleteTextures ( 1 , & pImage - > texnum ) ;
R_Free ( pImage ) ;
}
}
2022-09-18 15:37:21 +00:00
/*
= = = = = = = = = = = = = = =
Upload32
= = = = = = = = = = = = = = =
*/
2022-09-20 21:08:09 +00:00
extern qboolean charSet ;
2022-09-18 15:37:21 +00:00
static void Upload32 ( unsigned * data ,
2022-09-20 21:08:09 +00:00
GLenum format ,
qboolean mipmap ,
qboolean picmip ,
qboolean isLightmap ,
qboolean allowTC ,
int * pformat ,
word * pUploadWidth , word * pUploadHeight , bool bRectangle = false )
2022-09-18 15:37:21 +00:00
{
2022-09-20 21:08:09 +00:00
GLuint uiTarget = GL_TEXTURE_2D ;
# ifndef HAVE_GLES
if ( bRectangle )
{
uiTarget = GL_TEXTURE_RECTANGLE_EXT ;
}
# else
//Humm..
# endif
2022-09-18 15:37:21 +00:00
if ( format = = GL_RGBA )
{
2022-09-20 21:08:09 +00:00
int samples ;
int i , c ;
byte * scan ;
float rMax = 0 , gMax = 0 , bMax = 0 ;
int width = * pUploadWidth ;
int height = * pUploadHeight ;
//
// perform optional picmip operation
//
if ( picmip ) {
for ( i = 0 ; i < r_picmip - > integer ; i + + ) {
if ( ( width > 16 ) & & ( height > 16 ) ) {
R_MipMap ( ( byte * ) data , width , height ) ;
width > > = 1 ;
height > > = 1 ;
if ( width < 1 ) {
width = 1 ;
}
if ( height < 1 ) {
height = 1 ;
}
}
}
}
//
// clamp to the current upper OpenGL limit
// scale both axis down equally so we don't have to
// deal with a half mip resampling
//
while ( width > glConfig . maxTextureSize | | height > glConfig . maxTextureSize ) {
R_MipMap ( ( byte * ) data , width , height ) ;
width > > = 1 ;
height > > = 1 ;
}
//
// scan the texture for each channel's max values
// and verify if the alpha channel is being used or not
//
c = width * height ;
scan = ( ( byte * ) data ) ;
samples = 3 ;
for ( i = 0 ; i < c ; i + + )
{
if ( scan [ i * 4 + 0 ] > rMax )
{
rMax = scan [ i * 4 + 0 ] ;
}
if ( scan [ i * 4 + 1 ] > gMax )
{
gMax = scan [ i * 4 + 1 ] ;
}
if ( scan [ i * 4 + 2 ] > bMax )
{
bMax = scan [ i * 4 + 2 ] ;
}
if ( scan [ i * 4 + 3 ] ! = 255 )
{
samples = 4 ;
break ;
}
}
// select proper internal format
if ( samples = = 3 )
{
if ( glConfig . textureCompression = = TC_S3TC & & allowTC )
{
# ifdef HAVE_GLES
assert ( 0 ) ;
# else
* pformat = GL_RGB4_S3TC ;
# endif
}
else if ( glConfig . textureCompression = = TC_S3TC_DXT & & allowTC )
{ // Compress purely color - no alpha
if ( r_texturebits - > integer = = 16 ) {
# ifdef HAVE_GLES
assert ( 0 ) ;
# else
* pformat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT ; //this format cuts to 16 bit
# endif
}
else { //if we aren't using 16 bit then, use 32 bit compression
# ifdef HAVE_GLES
assert ( 0 ) ;
# else
* pformat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT ;
# endif
}
}
else if ( isLightmap & & r_texturebitslm - > integer > 0 )
{
int lmBits = r_texturebitslm - > integer & 0x30 ; // 16 or 32
// Allow different bit depth when we are a lightmap
if ( lmBits = = 16 )
* pformat = GL_RGB5 ;
else
# ifdef HAVE_GLES
* pformat = GL_RGB ;
# else
2022-11-23 22:17:58 +00:00
* pformat = GL_RGB8 ;
2022-09-20 21:08:09 +00:00
# endif
}
else if ( r_texturebits - > integer = = 16 )
{
* pformat = GL_RGB5 ;
}
else if ( r_texturebits - > integer = = 32 )
{
# ifdef HAVE_GLES
* pformat = GL_RGB ;
# else
* pformat = GL_RGB8 ;
# endif
}
else
{
# ifdef HAVE_GLES
* pformat = GL_RGB ;
# else
* pformat = 3 ;
# endif
}
}
else if ( samples = = 4 )
{
if ( glConfig . textureCompression = = TC_S3TC_DXT & & allowTC )
{ // Compress both alpha and color
# ifdef HAVE_GLES
assert ( 0 ) ;
# else
* pformat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT ;
# endif
}
else if ( r_texturebits - > integer = = 16 )
{
* pformat = GL_RGBA4 ;
}
else if ( r_texturebits - > integer = = 32 )
{
# ifdef HAVE_GLES
* pformat = GL_RGBA ;
# else
* pformat = GL_RGBA8 ;
# endif
}
else
{
# ifdef HAVE_GLES
* pformat = GL_RGBA ;
# else
* pformat = 4 ;
# endif
}
}
2022-09-18 15:37:21 +00:00
* pUploadWidth = width ;
* pUploadHeight = height ;
2022-09-20 21:08:09 +00:00
// copy or resample data as appropriate for first MIP level
# ifdef HAVE_GLES
2022-11-23 22:17:58 +00:00
# ifdef PANDORA
2022-09-20 21:08:09 +00:00
// SGX Workaround#2 : if width or height<=16 => make it bigger
2022-11-23 22:17:58 +00:00
# endif
2022-09-20 21:08:09 +00:00
//*pformat = GL_RGBA;
R_LightScaleTexture ( data , width , height , mipmap ? qfalse : qtrue ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_GENERATE_MIPMAP , ( mipmap ) ? GL_TRUE : GL_FALSE ) ;
// and now, convert if needed and upload
// GLES doesn't do convertion itself, so we have to handle that
byte * temp ;
switch ( * pformat ) {
2022-11-23 22:17:58 +00:00
case GL_RGB5 :
temp = gles_convertRGB5 ( ( byte * ) data , width , height ) ;
qglTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGB , width , height , 0 , GL_RGB , GL_UNSIGNED_SHORT_5_6_5 , temp ) ;
R_Free ( temp ) ;
break ;
case GL_RGBA4 :
temp = gles_convertRGBA4 ( ( byte * ) data , width , height ) ;
qglTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , width , height , 0 , GL_RGBA , GL_UNSIGNED_SHORT_4_4_4_4 , temp ) ;
R_Free ( temp ) ;
break ;
case 3 :
case GL_RGB :
temp = gles_convertRGB ( ( byte * ) data , width , height ) ;
qglTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGB , width , height , 0 , GL_RGB , GL_UNSIGNED_BYTE , temp ) ;
R_Free ( temp ) ;
break ;
case 1 :
temp = gles_convertLuminance ( ( byte * ) data , width , height ) ;
qglTexImage2D ( GL_TEXTURE_2D , 0 , GL_LUMINANCE , width , height , 0 , GL_LUMINANCE , GL_UNSIGNED_BYTE , temp ) ;
R_Free ( temp ) ;
break ;
case 2 :
temp = gles_convertLuminanceAlpha ( ( byte * ) data , width , height ) ;
qglTexImage2D ( GL_TEXTURE_2D , 0 , GL_LUMINANCE_ALPHA , width , height , 0 , GL_LUMINANCE_ALPHA , GL_UNSIGNED_BYTE , temp ) ;
R_Free ( temp ) ;
break ;
default :
* pformat = GL_RGBA ;
qglTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , width , height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , data ) ;
2022-09-20 21:08:09 +00:00
}
2022-11-23 22:17:58 +00:00
// qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
// qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
2022-09-20 21:08:09 +00:00
# else
if ( ! mipmap )
{
qglTexImage2D ( uiTarget , 0 , * pformat , width , height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , data ) ;
goto done ;
}
R_LightScaleTexture ( data , width , height , ( qboolean ) ! mipmap ) ;
qglTexImage2D ( uiTarget , 0 , * pformat , width , height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , data ) ;
if ( mipmap )
{
int miplevel ;
miplevel = 0 ;
while ( width > 1 | | height > 1 )
{
R_MipMap ( ( byte * ) data , width , height ) ;
width > > = 1 ;
height > > = 1 ;
if ( width < 1 )
width = 1 ;
if ( height < 1 )
height = 1 ;
miplevel + + ;
if ( r_colorMipLevels - > integer )
{
R_BlendOverTexture ( ( byte * ) data , width * height , mipBlendColors [ miplevel ] ) ;
}
qglTexImage2D ( uiTarget , miplevel , * pformat , width , height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , data ) ;
}
}
# endif
2022-09-18 15:37:21 +00:00
}
else
{
}
2022-09-20 21:08:09 +00:00
done :
2022-09-18 15:37:21 +00:00
if ( mipmap )
{
2022-09-20 21:08:09 +00:00
qglTexParameterf ( uiTarget , GL_TEXTURE_MIN_FILTER , gl_filter_min ) ;
qglTexParameterf ( uiTarget , GL_TEXTURE_MAG_FILTER , gl_filter_max ) ;
if ( r_ext_texture_filter_anisotropic - > integer > 1 & & glConfig . maxTextureFilterAnisotropy > 0 )
2022-09-18 15:37:21 +00:00
{
qglTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_MAX_ANISOTROPY_EXT , r_ext_texture_filter_anisotropic - > value ) ;
}
}
else
{
2022-09-20 21:08:09 +00:00
qglTexParameterf ( uiTarget , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ;
qglTexParameterf ( uiTarget , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
2022-09-18 15:37:21 +00:00
}
GL_CheckErrors ( ) ;
}
static void GL_ResetBinds ( void )
{
memset ( glState . currenttextures , 0 , sizeof ( glState . currenttextures ) ) ;
if ( qglActiveTextureARB ) {
GL_SelectTexture ( 1 ) ;
qglBindTexture ( GL_TEXTURE_2D , 0 ) ;
GL_SelectTexture ( 0 ) ;
qglBindTexture ( GL_TEXTURE_2D , 0 ) ;
} else {
qglBindTexture ( GL_TEXTURE_2D , 0 ) ;
}
}
2022-11-23 22:17:58 +00:00
2022-09-18 15:37:21 +00:00
// special function used in conjunction with "devmapbsp"...
//
2022-11-23 22:17:58 +00:00
// (avoid using ri.xxxx stuff here in case running on dedicated)
//
2022-09-18 15:37:21 +00:00
void R_Images_DeleteLightMaps ( void )
{
for ( AllocatedImages_t : : iterator itImage = AllocatedImages . begin ( ) ; itImage ! = AllocatedImages . end ( ) ; /* empty */ )
{
image_t * pImage = ( * itImage ) . second ;
2022-11-23 22:17:58 +00:00
if ( pImage - > imgName [ 0 ] = = ' * ' & & strstr ( pImage - > imgName , " lightmap " ) ) // loose check, but should be ok
2022-09-18 15:37:21 +00:00
{
R_Images_DeleteImageContents ( pImage ) ;
AllocatedImages . erase ( itImage + + ) ;
}
else
{
+ + itImage ;
}
}
GL_ResetBinds ( ) ;
}
// special function currently only called by Dissolve code...
//
void R_Images_DeleteImage ( image_t * pImage )
{
// Even though we supply the image handle, we need to get the corresponding iterator entry...
//
AllocatedImages_t : : iterator itImage = AllocatedImages . find ( pImage - > imgName ) ;
if ( itImage ! = AllocatedImages . end ( ) )
{
R_Images_DeleteImageContents ( pImage ) ;
AllocatedImages . erase ( itImage ) ;
}
else
{
assert ( 0 ) ;
}
}
// called only at app startup, vid_restart, app-exit
//
void R_Images_Clear ( void )
{
image_t * pImage ;
// int iNumImages =
2022-11-23 22:17:58 +00:00
R_Images_StartIteration ( ) ;
2022-09-18 15:37:21 +00:00
while ( ( pImage = R_Images_GetNextIteration ( ) ) ! = NULL )
{
R_Images_DeleteImageContents ( pImage ) ;
}
AllocatedImages . clear ( ) ;
2022-11-23 22:17:58 +00:00
2022-09-18 15:37:21 +00:00
giTextureBindNum = 1024 ;
}
void RE_RegisterImages_Info_f ( void )
{
image_t * pImage = NULL ;
int iImage = 0 ;
int iTexels = 0 ;
int iNumImages = R_Images_StartIteration ( ) ;
while ( ( pImage = R_Images_GetNextIteration ( ) ) ! = NULL )
{
ri . Printf ( PRINT_ALL , " %d: (%4dx%4dy) \" %s \" " , iImage , pImage - > width , pImage - > height , pImage - > imgName ) ;
2022-11-23 22:17:58 +00:00
ri . Printf ( PRINT_DEVELOPER , S_COLOR_RED " , levused %d " , pImage - > iLastLevelUsedOn ) ;
2022-09-18 15:37:21 +00:00
ri . Printf ( PRINT_ALL , " \n " ) ;
iTexels + = pImage - > width * pImage - > height ;
iImage + + ;
}
ri . Printf ( PRINT_ALL , " %d Images. %d (%.2fMB) texels total, (not including mipmaps) \n " , iNumImages , iTexels , ( float ) iTexels / 1024.0f / 1024.0f ) ;
2022-11-23 22:17:58 +00:00
ri . Printf ( PRINT_DEVELOPER , S_COLOR_RED " RE_RegisterMedia_GetLevel(): %d " , RE_RegisterMedia_GetLevel ( ) ) ;
2022-09-18 15:37:21 +00:00
}
// currently, this just goes through all the images and dumps any not referenced on this level...
//
qboolean RE_RegisterImages_LevelLoadEnd ( void )
{
2022-11-23 22:17:58 +00:00
ri . Printf ( PRINT_DEVELOPER , S_COLOR_RED " RE_RegisterImages_LevelLoadEnd(): \n " ) ;
// int iNumImages = AllocatedImages.size(); // more for curiosity, really.
2022-09-18 15:37:21 +00:00
qboolean imageDeleted = qtrue ;
for ( AllocatedImages_t : : iterator itImage = AllocatedImages . begin ( ) ; itImage ! = AllocatedImages . end ( ) ; /* blank */ )
{
qboolean bEraseOccured = qfalse ;
image_t * pImage = ( * itImage ) . second ;
2022-11-23 22:17:58 +00:00
// don't un-register system shaders (*fog, *dlight, *white, *default), but DO de-register lightmaps ("*<mapname>/lightmap%d")
if ( pImage - > imgName [ 0 ] ! = ' * ' | | strchr ( pImage - > imgName , ' / ' ) )
2022-09-18 15:37:21 +00:00
{
// image used on this level?
//
if ( pImage - > iLastLevelUsedOn ! = RE_RegisterMedia_GetLevel ( ) )
2022-11-23 22:17:58 +00:00
{
// nope, so dump it...
//
ri . Printf ( PRINT_DEVELOPER , S_COLOR_RED " Dumping image \" %s \" \n " , pImage - > imgName ) ;
2022-09-18 15:37:21 +00:00
R_Images_DeleteImageContents ( pImage ) ;
AllocatedImages . erase ( itImage + + ) ;
bEraseOccured = qtrue ;
imageDeleted = qtrue ;
}
}
if ( ! bEraseOccured )
{
+ + itImage ;
}
}
2022-11-23 22:17:58 +00:00
// this check can be deleted AFAIC, it seems to be just a quake thing...
//
// iNumImages = R_Images_StartIteration();
// if (iNumImages > MAX_DRAWIMAGES)
// {
// ri.Printf( PRINT_ALL, S_COLOR_YELLOW "Level uses %d images, old limit was MAX_DRAWIMAGES (%d)\n", iNumImages, MAX_DRAWIMAGES);
// }
ri . Printf ( PRINT_DEVELOPER , S_COLOR_RED " RE_RegisterImages_LevelLoadEnd(): Ok \n " ) ;
2022-09-18 15:37:21 +00:00
GL_ResetBinds ( ) ;
return imageDeleted ;
}
// returns image_t struct if we already have this, else NULL. No disk-open performed
// (important for creating default images).
//
// This is called by both R_FindImageFile and anything that creates default images...
//
static image_t * R_FindImageFile_NoLoad ( const char * name , qboolean mipmap , qboolean allowPicmip , qboolean allowTC , int glWrapClampMode )
{
if ( ! name ) {
return NULL ;
}
char * pName = GenerateImageMappingName ( name ) ;
//
// see if the image is already loaded
//
AllocatedImages_t : : iterator itAllocatedImage = AllocatedImages . find ( pName ) ;
if ( itAllocatedImage ! = AllocatedImages . end ( ) )
{
image_t * pImage = ( * itAllocatedImage ) . second ;
// the white image can be used with any set of parms, but other mismatches are errors...
//
if ( strcmp ( pName , " *white " ) ) {
if ( pImage - > mipmap ! = ! ! mipmap ) {
2022-11-23 22:17:58 +00:00
ri . Printf ( PRINT_ALL , S_COLOR_YELLOW " WARNING: reused image %s with mixed mipmap parm \n " , pName ) ;
2022-09-18 15:37:21 +00:00
}
if ( pImage - > allowPicmip ! = ! ! allowPicmip ) {
2022-11-23 22:17:58 +00:00
ri . Printf ( PRINT_ALL , S_COLOR_YELLOW " WARNING: reused image %s with mixed allowPicmip parm \n " , pName ) ;
2022-09-18 15:37:21 +00:00
}
if ( pImage - > wrapClampMode ! = glWrapClampMode ) {
2022-11-23 22:17:58 +00:00
ri . Printf ( PRINT_ALL , S_COLOR_YELLOW " WARNING: reused image %s with mixed glWrapClampMode parm \n " , pName ) ;
2022-09-18 15:37:21 +00:00
}
}
pImage - > iLastLevelUsedOn = RE_RegisterMedia_GetLevel ( ) ;
return pImage ;
}
return NULL ;
}
2022-11-23 22:17:58 +00:00
2022-09-18 15:37:21 +00:00
/*
= = = = = = = = = = = = = = = =
R_CreateImage
This is the only way any image_t are created
= = = = = = = = = = = = = = = =
*/
image_t * R_CreateImage ( const char * name , const byte * pic , int width , int height ,
2022-11-23 22:17:58 +00:00
GLenum format , qboolean mipmap , qboolean allowPicmip , qboolean allowTC , int glWrapClampMode , bool bRectangle )
2022-09-18 15:37:21 +00:00
{
image_t * image ;
qboolean isLightmap = qfalse ;
if ( strlen ( name ) > = MAX_QPATH ) {
Com_Error ( ERR_DROP , " R_CreateImage: \" %s \" is too long \n " , name ) ;
}
if ( glConfig . clampToEdgeAvailable & & glWrapClampMode = = GL_CLAMP ) {
glWrapClampMode = GL_CLAMP_TO_EDGE ;
}
2022-11-23 22:17:58 +00:00
if ( name [ 0 ] = = ' * ' )
2022-09-18 15:37:21 +00:00
{
2022-11-23 22:17:58 +00:00
const char * psLightMapNameSearchPos = strrchr ( name , ' / ' ) ;
if ( psLightMapNameSearchPos & & ! strncmp ( psLightMapNameSearchPos + 1 , " lightmap " , 8 ) ) {
isLightmap = qtrue ;
}
2022-09-18 15:37:21 +00:00
}
if ( ( width & ( width - 1 ) ) | | ( height & ( height - 1 ) ) )
{
Com_Error ( ERR_FATAL , " R_CreateImage: %s dimensions (%i x %i) not power of 2! \n " , name , width , height ) ;
}
image = R_FindImageFile_NoLoad ( name , mipmap , allowPicmip , allowTC , glWrapClampMode ) ;
if ( image ) {
return image ;
}
image = ( image_t * ) R_Malloc ( sizeof ( image_t ) , TAG_IMAGE_T , qtrue ) ;
2022-11-23 22:17:58 +00:00
// memset(image,0,sizeof(*image)); // qtrue above does this
2022-09-18 15:37:21 +00:00
image - > texnum = 1024 + giTextureBindNum + + ; // ++ is of course staggeringly important...
// record which map it was used on...
//
image - > iLastLevelUsedOn = RE_RegisterMedia_GetLevel ( ) ;
image - > mipmap = ! ! mipmap ;
image - > allowPicmip = ! ! allowPicmip ;
Q_strncpyz ( image - > imgName , name , sizeof ( image - > imgName ) ) ;
image - > width = width ;
image - > height = height ;
image - > wrapClampMode = glWrapClampMode ;
if ( qglActiveTextureARB ) {
GL_SelectTexture ( 0 ) ;
}
2022-11-23 22:17:58 +00:00
GLuint uiTarget = GL_TEXTURE_2D ;
# ifndef HAVE_GLES
if ( bRectangle )
{
qglDisable ( uiTarget ) ;
uiTarget = GL_TEXTURE_RECTANGLE_EXT ;
qglEnable ( uiTarget ) ;
glWrapClampMode = GL_CLAMP_TO_EDGE ; // default mode supported by rectangle.
qglBindTexture ( uiTarget , image - > texnum ) ;
}
else
# endif
{
GL_Bind ( image ) ;
}
2022-09-18 15:37:21 +00:00
Upload32 ( ( unsigned * ) pic , format ,
2022-11-23 22:17:58 +00:00
( qboolean ) image - > mipmap ,
allowPicmip ,
isLightmap ,
allowTC ,
& image - > internalFormat ,
& image - > width ,
& image - > height , bRectangle ) ;
qglTexParameterf ( uiTarget , GL_TEXTURE_WRAP_S , glWrapClampMode ) ;
qglTexParameterf ( uiTarget , GL_TEXTURE_WRAP_T , glWrapClampMode ) ;
qglBindTexture ( uiTarget , 0 ) ; //jfm: i don't know why this is here, but it breaks lightmaps when there's only 1
2022-09-18 15:37:21 +00:00
glState . currenttextures [ glState . currenttmu ] = 0 ; //mark it not bound
const char * psNewName = GenerateImageMappingName ( name ) ;
Q_strncpyz ( image - > imgName , psNewName , sizeof ( image - > imgName ) ) ;
AllocatedImages [ image - > imgName ] = image ;
2022-11-23 22:17:58 +00:00
if ( bRectangle )
{
qglDisable ( uiTarget ) ;
qglEnable ( GL_TEXTURE_2D ) ;
}
2022-09-18 15:37:21 +00:00
return image ;
}
2022-11-23 22:17:58 +00:00
//rwwRMG - added
void R_CreateAutomapImage ( const char * name , const byte * pic , int width , int height , qboolean mipmap , qboolean allowPicmip , qboolean allowTC , int glWrapClampMode )
{
R_CreateImage ( name , pic , width , height , GL_RGBA , mipmap , allowPicmip , allowTC , glWrapClampMode ) ;
}
2022-09-18 15:37:21 +00:00
/*
= = = = = = = = = = = = = = =
R_FindImageFile
Finds or loads the given image .
Returns NULL if it fails , not a default image .
= = = = = = = = = = = = = =
*/
image_t * R_FindImageFile ( const char * name , qboolean mipmap , qboolean allowPicmip , qboolean allowTC , int glWrapClampMode ) {
image_t * image ;
int width , height ;
byte * pic ;
2022-11-23 22:17:58 +00:00
if ( ! name | | ri . Cvar_VariableIntegerValue ( " dedicated " ) ) // stop ghoul2 horribleness as regards image loading from server
{
2022-09-18 15:37:21 +00:00
return NULL ;
}
// need to do this here as well as in R_CreateImage, or R_FindImageFile_NoLoad() may complain about
// different clamp parms used...
//
if ( glConfig . clampToEdgeAvailable & & glWrapClampMode = = GL_CLAMP ) {
glWrapClampMode = GL_CLAMP_TO_EDGE ;
}
image = R_FindImageFile_NoLoad ( name , mipmap , allowPicmip , allowTC , glWrapClampMode ) ;
if ( image ) {
return image ;
}
//
// load the pic from disk
//
R_LoadImage ( name , & pic , & width , & height ) ;
2022-11-23 22:17:58 +00:00
if ( pic = = NULL ) { // if we dont get a successful load
return NULL ; // bail
}
// refuse to find any files not power of 2 dims...
//
if ( ( width & ( width - 1 ) ) | | ( height & ( height - 1 ) ) )
{
ri . Printf ( PRINT_ALL , " Refusing to load non-power-2-dims(%d,%d) pic \" %s \" ... \n " , width , height , name ) ;
return NULL ;
2022-09-18 15:37:21 +00:00
}
image = R_CreateImage ( ( char * ) name , pic , width , height , GL_RGBA , mipmap , allowPicmip , allowTC , glWrapClampMode ) ;
R_Free ( pic ) ;
return image ;
}
/*
= = = = = = = = = = = = = = = =
R_CreateDlightImage
= = = = = = = = = = = = = = = =
*/
2022-11-23 22:17:58 +00:00
# define DLIGHT_SIZE 16
2022-09-18 15:37:21 +00:00
static void R_CreateDlightImage ( void )
{
int width , height ;
byte * pic ;
R_LoadImage ( " gfx/2d/dlight " , & pic , & width , & height ) ;
if ( pic )
{
tr . dlightImage = R_CreateImage ( " *dlight " , pic , width , height , GL_RGBA , qfalse , qfalse , qfalse , GL_CLAMP ) ;
R_Free ( pic ) ;
}
else
{ // if we dont get a successful load
int x , y ;
byte data [ DLIGHT_SIZE ] [ DLIGHT_SIZE ] [ 4 ] ;
int b ;
// make a centered inverse-square falloff blob for dynamic lighting
for ( x = 0 ; x < DLIGHT_SIZE ; x + + ) {
for ( y = 0 ; y < DLIGHT_SIZE ; y + + ) {
float d ;
d = ( DLIGHT_SIZE / 2 - 0.5f - x ) * ( DLIGHT_SIZE / 2 - 0.5f - x ) +
( DLIGHT_SIZE / 2 - 0.5f - y ) * ( DLIGHT_SIZE / 2 - 0.5f - y ) ;
b = 4000 / d ;
if ( b > 255 ) {
b = 255 ;
} else if ( b < 75 ) {
b = 0 ;
}
data [ y ] [ x ] [ 0 ] =
2022-11-23 22:17:58 +00:00
data [ y ] [ x ] [ 1 ] =
data [ y ] [ x ] [ 2 ] = b ;
2022-09-18 15:37:21 +00:00
data [ y ] [ x ] [ 3 ] = 255 ;
}
}
tr . dlightImage = R_CreateImage ( " *dlight " , ( byte * ) data , DLIGHT_SIZE , DLIGHT_SIZE , GL_RGBA , qfalse , qfalse , qfalse , GL_CLAMP ) ;
}
}
2022-11-23 22:17:58 +00:00
2022-09-18 15:37:21 +00:00
/*
= = = = = = = = = = = = = = = = =
R_InitFogTable
= = = = = = = = = = = = = = = = =
*/
void R_InitFogTable ( void ) {
int i ;
float d ;
float exp ;
exp = 0.5 ;
for ( i = 0 ; i < FOG_TABLE_SIZE ; i + + ) {
d = pow ( ( float ) i / ( FOG_TABLE_SIZE - 1 ) , exp ) ;
tr . fogTable [ i ] = d ;
}
}
/*
= = = = = = = = = = = = = = = =
R_FogFactor
Returns a 0.0 to 1.0 fog density value
This is called for each texel of the fog texture on startup
and for each vertex of transparent shaders in fog dynamically
= = = = = = = = = = = = = = = =
*/
float R_FogFactor ( float s , float t ) {
float d ;
s - = 1.0 / 512 ;
if ( s < 0 ) {
return 0 ;
}
if ( t < 1.0 / 32 ) {
return 0 ;
}
if ( t < 31.0 / 32 ) {
s * = ( t - 1.0f / 32.0f ) / ( 30.0f / 32.0f ) ;
}
// we need to leave a lot of clamp range
s * = 8 ;
if ( s > 1.0 ) {
s = 1.0 ;
}
d = tr . fogTable [ ( int ) ( s * ( FOG_TABLE_SIZE - 1 ) ) ] ;
return d ;
}
/*
= = = = = = = = = = = = = = = =
R_CreateFogImage
= = = = = = = = = = = = = = = =
*/
# define FOG_S 256
# define FOG_T 32
static void R_CreateFogImage ( void ) {
int x , y ;
byte * data ;
float d ;
float borderColor [ 4 ] ;
2022-11-23 22:17:58 +00:00
data = ( unsigned char * ) R_Malloc ( FOG_S * FOG_T * 4 , TAG_TEMP_WORKSPACE , qfalse ) ;
2022-09-18 15:37:21 +00:00
// S is distance, T is depth
for ( x = 0 ; x < FOG_S ; x + + ) {
for ( y = 0 ; y < FOG_T ; y + + ) {
d = R_FogFactor ( ( x + 0.5f ) / FOG_S , ( y + 0.5f ) / FOG_T ) ;
data [ ( y * FOG_S + x ) * 4 + 0 ] =
data [ ( y * FOG_S + x ) * 4 + 1 ] =
data [ ( y * FOG_S + x ) * 4 + 2 ] = 255 ;
data [ ( y * FOG_S + x ) * 4 + 3 ] = 255 * d ;
}
}
// standard openGL clamping doesn't really do what we want -- it includes
// the border color at the edges. OpenGL 1.2 has clamp-to-edge, which does
// what we want.
2022-11-23 22:17:58 +00:00
tr . fogImage = R_CreateImage ( " *fog " , ( byte * ) data , FOG_S , FOG_T , GL_RGBA , qfalse , qfalse , qfalse , GL_CLAMP ) ;
2022-09-18 15:37:21 +00:00
R_Free ( data ) ;
borderColor [ 0 ] = 1.0 ;
borderColor [ 1 ] = 1.0 ;
borderColor [ 2 ] = 1.0 ;
borderColor [ 3 ] = 1 ;
2022-09-20 21:08:09 +00:00
# ifndef HAVE_GLES
2022-09-18 15:37:21 +00:00
qglTexParameterfv ( GL_TEXTURE_2D , GL_TEXTURE_BORDER_COLOR , borderColor ) ;
2022-09-20 21:08:09 +00:00
# endif
2022-09-18 15:37:21 +00:00
}
/*
= = = = = = = = = = = = = = = = = =
R_CreateDefaultImage
= = = = = = = = = = = = = = = = = =
*/
# define DEFAULT_SIZE 16
static void R_CreateDefaultImage ( void ) {
int x ;
byte data [ DEFAULT_SIZE ] [ DEFAULT_SIZE ] [ 4 ] ;
// the default image will be a box, to allow you to see the mapping coordinates
memset ( data , 32 , sizeof ( data ) ) ;
for ( x = 0 ; x < DEFAULT_SIZE ; x + + ) {
data [ 0 ] [ x ] [ 0 ] =
data [ 0 ] [ x ] [ 1 ] =
data [ 0 ] [ x ] [ 2 ] =
data [ 0 ] [ x ] [ 3 ] = 255 ;
data [ x ] [ 0 ] [ 0 ] =
data [ x ] [ 0 ] [ 1 ] =
data [ x ] [ 0 ] [ 2 ] =
data [ x ] [ 0 ] [ 3 ] = 255 ;
data [ DEFAULT_SIZE - 1 ] [ x ] [ 0 ] =
data [ DEFAULT_SIZE - 1 ] [ x ] [ 1 ] =
data [ DEFAULT_SIZE - 1 ] [ x ] [ 2 ] =
data [ DEFAULT_SIZE - 1 ] [ x ] [ 3 ] = 255 ;
data [ x ] [ DEFAULT_SIZE - 1 ] [ 0 ] =
data [ x ] [ DEFAULT_SIZE - 1 ] [ 1 ] =
data [ x ] [ DEFAULT_SIZE - 1 ] [ 2 ] =
data [ x ] [ DEFAULT_SIZE - 1 ] [ 3 ] = 255 ;
}
2022-11-23 22:17:58 +00:00
tr . defaultImage = R_CreateImage ( " *default " , ( byte * ) data , DEFAULT_SIZE , DEFAULT_SIZE , GL_RGBA , qtrue , qfalse , qfalse , GL_REPEAT ) ;
2022-09-18 15:37:21 +00:00
}
/*
= = = = = = = = = = = = = = = = = =
R_CreateBuiltinImages
= = = = = = = = = = = = = = = = = =
*/
void R_CreateBuiltinImages ( void ) {
int x , y ;
byte data [ DEFAULT_SIZE ] [ DEFAULT_SIZE ] [ 4 ] ;
R_CreateDefaultImage ( ) ;
// we use a solid white image instead of disabling texturing
memset ( data , 255 , sizeof ( data ) ) ;
2022-09-20 21:08:09 +00:00
tr . whiteImage = R_CreateImage ( " *white " , ( byte * ) data , 8 , 8 , GL_RGBA , qfalse , qfalse , qfalse , GL_REPEAT ) ;
2022-09-18 15:37:21 +00:00
tr . screenImage = R_CreateImage ( " *screen " , ( byte * ) data , 8 , 8 , GL_RGBA , qfalse , qfalse , qfalse , GL_REPEAT ) ;
2022-09-20 21:08:09 +00:00
# ifndef HAVE_GLES
2022-09-18 15:37:21 +00:00
// Create the scene glow image. - AReis
tr . screenGlow = 1024 + giTextureBindNum + + ;
qglDisable ( GL_TEXTURE_2D ) ;
2022-09-20 21:08:09 +00:00
qglEnable ( GL_TEXTURE_RECTANGLE_EXT ) ;
qglBindTexture ( GL_TEXTURE_RECTANGLE_EXT , tr . screenGlow ) ;
qglTexImage2D ( GL_TEXTURE_RECTANGLE_EXT , 0 , GL_RGBA16 , glConfig . vidWidth , glConfig . vidHeight , 0 , GL_RGB , GL_FLOAT , 0 ) ;
qglTexParameteri ( GL_TEXTURE_RECTANGLE_EXT , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ;
qglTexParameteri ( GL_TEXTURE_RECTANGLE_EXT , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
qglTexParameteri ( GL_TEXTURE_RECTANGLE_EXT , GL_TEXTURE_WRAP_S , GL_CLAMP ) ;
qglTexParameteri ( GL_TEXTURE_RECTANGLE_EXT , GL_TEXTURE_WRAP_T , GL_CLAMP ) ;
2022-09-18 15:37:21 +00:00
// Create the scene image. - AReis
tr . sceneImage = 1024 + giTextureBindNum + + ;
2022-09-20 21:08:09 +00:00
qglBindTexture ( GL_TEXTURE_RECTANGLE_EXT , tr . sceneImage ) ;
qglTexImage2D ( GL_TEXTURE_RECTANGLE_EXT , 0 , GL_RGBA16 , glConfig . vidWidth , glConfig . vidHeight , 0 , GL_RGB , GL_FLOAT , 0 ) ;
qglTexParameteri ( GL_TEXTURE_RECTANGLE_EXT , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ;
qglTexParameteri ( GL_TEXTURE_RECTANGLE_EXT , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
qglTexParameteri ( GL_TEXTURE_RECTANGLE_EXT , GL_TEXTURE_WRAP_S , GL_CLAMP ) ;
qglTexParameteri ( GL_TEXTURE_RECTANGLE_EXT , GL_TEXTURE_WRAP_T , GL_CLAMP ) ;
2022-09-18 15:37:21 +00:00
// Create the minimized scene blur image.
if ( r_DynamicGlowWidth - > integer > glConfig . vidWidth )
{
r_DynamicGlowWidth - > integer = glConfig . vidWidth ;
}
if ( r_DynamicGlowHeight - > integer > glConfig . vidHeight )
{
r_DynamicGlowHeight - > integer = glConfig . vidHeight ;
}
tr . blurImage = 1024 + giTextureBindNum + + ;
2022-09-20 21:08:09 +00:00
qglBindTexture ( GL_TEXTURE_RECTANGLE_EXT , tr . blurImage ) ;
qglTexImage2D ( GL_TEXTURE_RECTANGLE_EXT , 0 , GL_RGBA16 , r_DynamicGlowWidth - > integer , r_DynamicGlowHeight - > integer , 0 , GL_RGB , GL_FLOAT , 0 ) ;
qglTexParameteri ( GL_TEXTURE_RECTANGLE_EXT , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ;
qglTexParameteri ( GL_TEXTURE_RECTANGLE_EXT , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
qglTexParameteri ( GL_TEXTURE_RECTANGLE_EXT , GL_TEXTURE_WRAP_S , GL_CLAMP ) ;
qglTexParameteri ( GL_TEXTURE_RECTANGLE_EXT , GL_TEXTURE_WRAP_T , GL_CLAMP ) ;
qglDisable ( GL_TEXTURE_RECTANGLE_EXT ) ;
2022-09-18 15:37:21 +00:00
qglEnable ( GL_TEXTURE_2D ) ;
// with overbright bits active, we need an image which is some fraction of full color,
// for default lightmaps, etc
for ( x = 0 ; x < DEFAULT_SIZE ; x + + ) {
for ( y = 0 ; y < DEFAULT_SIZE ; y + + ) {
data [ y ] [ x ] [ 0 ] =
data [ y ] [ x ] [ 1 ] =
data [ y ] [ x ] [ 2 ] = tr . identityLightByte ;
data [ y ] [ x ] [ 3 ] = 255 ;
}
}
2022-09-20 21:08:09 +00:00
# endif // HAVE_GLES
2022-09-18 15:37:21 +00:00
2022-09-20 21:08:09 +00:00
tr . identityLightImage = R_CreateImage ( " *identityLight " , ( byte * ) data , 8 , 8 , GL_RGBA , qfalse , qfalse , qfalse , GL_REPEAT ) ;
2022-09-18 15:37:21 +00:00
for ( x = 0 ; x < NUM_SCRATCH_IMAGES ; x + + ) {
// scratchimage is usually used for cinematic drawing
2022-09-20 21:08:09 +00:00
tr . scratchImage [ x ] = R_CreateImage ( va ( " *scratch%d " , x ) , ( byte * ) data , DEFAULT_SIZE , DEFAULT_SIZE , GL_RGBA , qfalse , qtrue , qfalse , GL_CLAMP ) ;
2022-09-18 15:37:21 +00:00
}
R_CreateDlightImage ( ) ;
2022-09-20 21:08:09 +00:00
2022-09-18 15:37:21 +00:00
R_CreateFogImage ( ) ;
}
/*
= = = = = = = = = = = = = = =
R_SetColorMappings
= = = = = = = = = = = = = = =
*/
void R_SetColorMappings ( void ) {
int i , j ;
float g ;
int inf ;
int shift ;
// setup the overbright lighting
tr . overbrightBits = r_overBrightBits - > integer ;
if ( ! glConfig . deviceSupportsGamma ) {
tr . overbrightBits = 0 ; // need hardware gamma for overbright
}
// never overbright in windowed mode
if ( ! glConfig . isFullscreen )
{
tr . overbrightBits = 0 ;
}
if ( tr . overbrightBits > 1 ) {
tr . overbrightBits = 1 ;
}
2022-11-23 22:17:58 +00:00
2022-09-18 15:37:21 +00:00
if ( tr . overbrightBits < 0 ) {
tr . overbrightBits = 0 ;
}
2022-11-23 22:17:58 +00:00
tr . identityLight = 1.0f / ( 1 < < tr . overbrightBits ) ;
2022-09-18 15:37:21 +00:00
tr . identityLightByte = 255 * tr . identityLight ;
if ( r_intensity - > value < 1.0f ) {
2022-11-23 22:17:58 +00:00
ri . Cvar_Set ( " r_intensity " , " 1 " ) ;
2022-09-18 15:37:21 +00:00
}
if ( r_gamma - > value < 0.5f ) {
ri . Cvar_Set ( " r_gamma " , " 0.5 " ) ;
} else if ( r_gamma - > value > 3.0f ) {
ri . Cvar_Set ( " r_gamma " , " 3.0 " ) ;
}
g = r_gamma - > value ;
shift = tr . overbrightBits ;
for ( i = 0 ; i < 256 ; i + + ) {
if ( g = = 1 ) {
inf = i ;
} else {
inf = 255 * pow ( i / 255.0f , 1.0f / g ) + 0.5f ;
}
inf < < = shift ;
if ( inf < 0 ) {
inf = 0 ;
}
if ( inf > 255 ) {
inf = 255 ;
}
s_gammatable [ i ] = inf ;
}
for ( i = 0 ; i < 256 ; i + + ) {
j = i * r_intensity - > value ;
if ( j > 255 ) {
j = 255 ;
}
s_intensitytable [ i ] = j ;
}
if ( glConfig . deviceSupportsGamma )
{
ri . WIN_SetGamma ( & glConfig , s_gammatable , s_gammatable , s_gammatable ) ;
}
}
/*
= = = = = = = = = = = = = = =
R_InitImages
= = = = = = = = = = = = = = =
*/
void R_InitImages ( void ) {
//memset(hashTable, 0, sizeof(hashTable)); // DO NOT DO THIS NOW (because of image cacheing) -ste.
// build brightness translation tables
R_SetColorMappings ( ) ;
// create default texture and white texture
R_CreateBuiltinImages ( ) ;
}
/*
= = = = = = = = = = = = = = =
R_DeleteTextures
= = = = = = = = = = = = = = =
*/
// (only gets called during vid_restart now (and app exit), not during map load)
//
void R_DeleteTextures ( void ) {
R_Images_Clear ( ) ;
GL_ResetBinds ( ) ;
}