This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/render/r_image.c

1737 lines
44 KiB
C

/*
Copyright (C) 1997-2001 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "r_local.h"
#include "byteorder.h"
#include "mathlib.h"
#define MAX_GLIMAGES 4096
#define IMAGES_HASH_SIZE 64
static image_t images[MAX_GLIMAGES];
image_t *r_lightmapTextures[MAX_GLIMAGES];
static image_t *images_hash[IMAGES_HASH_SIZE];
static unsigned int image_cur_hash;
static int r_numImages;
static byte *r_texturesPool;
static char *r_imagePathBuf, *r_imagePathBuf2;
static size_t r_sizeof_imagePathBuf, r_sizeof_imagePathBuf2;
const vec3_t r_cubeMapAngles[6] =
{
{ 0, 180, 90},
{ 0, 0, 270},
{ 0, 90, 180},
{ 0, 270, 0},
{ -90, 270, 0},
{ 90, 90, 0}
};
const vec3_t r_skyBoxAngles[6] =
{
{ 0, 0, 0},
{ 0, 180, 0},
{ 0, 90, 0},
{ 0, 270, 0},
{ -90, 0, 0},
{ 90, 0, 0}
};
#undef ENSUREBUFSIZE
#define ENSUREBUFSIZE(buf,need) \
if( r_sizeof_ ##buf < need ) \
{ \
if( r_ ##buf ) \
Mem_Free( r_ ##buf ); \
r_sizeof_ ##buf += (((need) & (MAX_SHADERPATH-1))+1) * MAX_SHADERPATH; \
r_ ##buf = Mem_Alloc( r_texturesPool, r_sizeof_ ##buf ); \
}
int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST;
int gl_filter_max = GL_LINEAR;
int gl_filter_depth = GL_LINEAR;
void GL_SelectTexture( int tmu )
{
if( !glConfig.ext.multitexture )
return;
if( tmu == glState.currentTMU )
return;
glState.currentTMU = tmu;
if( pglActiveTextureARB )
{
pglActiveTextureARB( tmu + GL_TEXTURE0_ARB );
pglClientActiveTextureARB( tmu + GL_TEXTURE0_ARB );
}
else if( pglSelectTextureSGIS )
{
pglSelectTextureSGIS( tmu + GL_TEXTURE0_SGIS );
}
}
void GL_TexEnv( GLenum mode )
{
if( mode != ( GLenum )glState.currentEnvModes[glState.currentTMU] )
{
pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode );
glState.currentEnvModes[glState.currentTMU] = ( int )mode;
}
}
void GL_Bind( int tmu, image_t *tex )
{
GL_SelectTexture( tmu );
if( r_nobind->integer ) // performance evaluation option
tex = r_notexture;
if( glState.currentTextures[tmu] == tex->texnum )
return;
glState.currentTextures[tmu] = tex->texnum;
if( tex->flags & IT_CUBEMAP )
pglBindTexture( GL_TEXTURE_CUBE_MAP_ARB, tex->texnum );
else if( tex->depth != 1 )
pglBindTexture( GL_TEXTURE_3D, tex->texnum );
else
pglBindTexture( GL_TEXTURE_2D, tex->texnum );
}
void GL_LoadTexMatrix( const mat4x4_t m )
{
pglMatrixMode( GL_TEXTURE );
pglLoadMatrixf( m );
glState.texIdentityMatrix[glState.currentTMU] = false;
}
void GL_LoadIdentityTexMatrix( void )
{
if( !glState.texIdentityMatrix[glState.currentTMU] )
{
pglMatrixMode( GL_TEXTURE );
pglLoadIdentity();
glState.texIdentityMatrix[glState.currentTMU] = true;
}
}
void GL_EnableTexGen( int coord, int mode )
{
int tmu = glState.currentTMU;
int bit, gen;
switch( coord )
{
case GL_S:
bit = 1;
gen = GL_TEXTURE_GEN_S;
break;
case GL_T:
bit = 2;
gen = GL_TEXTURE_GEN_T;
break;
case GL_R:
bit = 4;
gen = GL_TEXTURE_GEN_R;
break;
case GL_Q:
bit = 8;
gen = GL_TEXTURE_GEN_Q;
break;
default:
return;
}
if( mode )
{
if( !( glState.genSTEnabled[tmu] & bit ) )
{
pglEnable( gen );
glState.genSTEnabled[tmu] |= bit;
}
pglTexGeni( coord, GL_TEXTURE_GEN_MODE, mode );
}
else
{
if( glState.genSTEnabled[tmu] & bit )
{
pglDisable( gen );
glState.genSTEnabled[tmu] &= ~bit;
}
}
}
void GL_SetTexCoordArrayMode( int mode )
{
int tmu = glState.currentTMU;
int bit, cmode = glState.texCoordArrayMode[tmu];
if( mode == GL_TEXTURE_COORD_ARRAY )
bit = 1;
else if( mode == GL_TEXTURE_CUBE_MAP_ARB )
bit = 2;
else
bit = 0;
if( cmode != bit )
{
if( cmode == 1 )
pglDisableClientState( GL_TEXTURE_COORD_ARRAY );
else if( cmode == 2 )
pglDisable( GL_TEXTURE_CUBE_MAP_ARB );
if( bit == 1 )
pglEnableClientState( GL_TEXTURE_COORD_ARRAY );
else if( bit == 2 )
pglEnable( GL_TEXTURE_CUBE_MAP_ARB );
glState.texCoordArrayMode[tmu] = bit;
}
}
typedef struct
{
char *name;
int minimize, maximize;
} glmode_t;
glmode_t modes[] = {
{ "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 }
};
#define NUM_GL_MODES ( sizeof( modes ) / sizeof( glmode_t ) )
/*
===============
R_TextureMode
===============
*/
void R_TextureMode( char *string )
{
int i;
image_t *glt;
for( i = 0; i < NUM_GL_MODES; i++ )
{
if( !com.stricmp( modes[i].name, string ) )
break;
}
if( i == NUM_GL_MODES )
{
Msg( "R_TextureMode: bad filter name\n" );
return;
}
gl_filter_min = modes[i].minimize;
gl_filter_max = modes[i].maximize;
// change all the existing mipmap texture objects
for( i = 1, glt = images; i < r_numImages; i++, glt++ )
{
GL_Bind( 0, glt );
if( glt->flags & IT_DEPTH )
{
pglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_depth );
pglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_depth );
}
else if( !( glt->flags & IT_NOMIPMAP ) )
{
pglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min );
pglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max );
}
else
{
pglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max );
pglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max );
}
}
}
/*
===============
R_ImageList_f
===============
*/
void R_ImageList_f( void )
{
int i, num_depth = 0;
image_t *image;
double texels = 0, add;
double depth_texels = 0;
Msg( "------------------\n" );
for( i = 0, image = images; i < r_numImages; i++, image++ )
{
if( !image->upload_width || !image->upload_height || !image->upload_depth )
continue;
if( image->flags & IT_NOMIPMAP )
add = image->upload_width * image->upload_height * image->upload_depth;
else
add = (unsigned)ceil( (double)image->upload_width * image->upload_height * image->upload_depth / 0.75 );
if( image->flags & IT_CUBEMAP )
add *= 6;
if( image->flags & IT_DEPTH )
{
num_depth++;
depth_texels += image->upload_width * image->upload_height * image->upload_depth;
Msg( " %4i %4i %4i: %s\n", image->upload_width, image->upload_height, image->upload_depth,
image->name );
}
else
{
texels += add;
Msg( " %4i %4i %4i: %s%s%s%s %.1f KB\n", image->upload_width, image->upload_height, image->upload_depth,
(image->flags & IT_NORGB ? "*A" : (image->flags & IT_NOALPHA ? "*C" : "")),
image->name, image->extension, ((image->flags & IT_NOMIPMAP) ? "" : " (M)"), add / 1024.0 );
}
}
Msg( "Total texels count (counting mipmaps, approx): %.0f\n", texels );
Msg( "%i RGBA images, totalling %.3f megabytes\n", r_numImages - 1, texels / (1048576.0 / 4.0) );
if( num_depth )
Msg( "%i depth images, totalling %.0f texels\n", num_depth, depth_texels );
}
/*
=================================================================
TEMPORARY IMAGE BUFFERS
=================================================================
*/
enum
{
TEXTURE_LOADING_BUF0,TEXTURE_LOADING_BUF1,TEXTURE_LOADING_BUF2,TEXTURE_LOADING_BUF3,TEXTURE_LOADING_BUF4,TEXTURE_LOADING_BUF5,
TEXTURE_RESAMPLING_BUF,
TEXTURE_LINE_BUF,
TEXTURE_FLIPPING_BUF0,TEXTURE_FLIPPING_BUF1,TEXTURE_FLIPPING_BUF2,TEXTURE_FLIPPING_BUF3,TEXTURE_FLIPPING_BUF4,TEXTURE_FLIPPING_BUF5,
NUM_IMAGE_BUFFERS
};
static byte *r_aviBuffer;
static byte *r_imageBuffers[NUM_IMAGE_BUFFERS];
static size_t r_imageBufSize[NUM_IMAGE_BUFFERS];
#define R_PrepareImageBuffer(buffer,size) _R_PrepareImageBuffer(buffer,size,__FILE__,__LINE__)
/*
==============
R_PrepareImageBuffer
==============
*/
static byte *_R_PrepareImageBuffer( int buffer, size_t size, const char *filename, int fileline )
{
if( r_imageBufSize[buffer] < size )
{
r_imageBufSize[buffer] = size;
if( r_imageBuffers[buffer] )
Mem_Free( r_imageBuffers[buffer] );
r_imageBuffers[buffer] = Mem_Alloc( r_texturesPool, size );
}
memset( r_imageBuffers[buffer], 255, size );
return r_imageBuffers[buffer];
}
/*
==============
R_FreeImageBuffers
==============
*/
void R_FreeImageBuffers( void )
{
int i;
for( i = 0; i < NUM_IMAGE_BUFFERS; i++ )
{
if( r_imageBuffers[i] )
{
Mem_Free( r_imageBuffers[i] );
r_imageBuffers[i] = NULL;
}
r_imageBufSize[i] = 0;
}
}
//=======================================================
int R_GetSamples( int flags )
{
if( flags & IMAGE_HAS_COLOR )
return (flags & IMAGE_HAS_ALPHA) ? 4 : 3;
return (flags & IMAGE_HAS_ALPHA) ? 2 : 1;
}
/*
===============
R_LoadImageFromDisk
===============
*/
static int R_LoadImageFromDisk( char *pathname, size_t pathname_size, byte **pic, int *width, int *height, int side )
{
rgbdata_t *image;
int samples = 0;
*pic = NULL;
*width = *height = 0;
image = FS_LoadImage( pathname, NULL, 0 );
if( image )
{
samples = R_GetSamples( image->flags );
*pic = image->buffer;
*width = image->width;
*height = image->height;
}
// FIXME: need to release rgbdata
return samples;
}
/*
================
R_FlipTexture
================
*/
static void R_FlipTexture( const byte *in, byte *out, int width, int height, int samples, bool flipx, bool flipy, bool flipdiagonal )
{
int i, x, y;
const byte *p, *line;
int row_inc = ( flipy ? -samples : samples ) * width, col_inc = ( flipx ? -samples : samples );
int row_ofs = ( flipy ? ( height - 1 ) * width * samples : 0 ), col_ofs = ( flipx ? ( width - 1 ) * samples : 0 );
if( flipdiagonal )
{
for( x = 0, line = in + col_ofs; x < width; x++, line += col_inc )
for( y = 0, p = line + row_ofs; y < height; y++, p += row_inc, out += samples )
for( i = 0; i < samples; i++ )
out[i] = p[i];
}
else
{
for( y = 0, line = in + row_ofs; y < height; y++, line += row_inc )
for( x = 0, p = line + col_ofs; x < width; x++, p += col_inc, out += samples )
for( i = 0; i < samples; i++ )
out[i] = p[i];
}
}
/*
================
R_ResampleTexture
================
*/
static void R_ResampleTexture( const unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight )
{
int i, j;
const unsigned *inrow, *inrow2;
unsigned frac, fracstep;
unsigned *p1, *p2;
byte *pix1, *pix2, *pix3, *pix4;
if( inwidth == outwidth && inheight == outheight )
{
memcpy( out, in, inwidth * inheight * 4 );
return;
}
p1 = ( unsigned * )R_PrepareImageBuffer( TEXTURE_LINE_BUF, outwidth * 1 * sizeof( unsigned ) * 2 );
p2 = p1 + outwidth;
fracstep = inwidth * 0x10000 / outwidth;
frac = fracstep >> 2;
for( i = 0; i < outwidth; i++ )
{
p1[i] = 4 * ( frac >> 16 );
frac += fracstep;
}
frac = 3 * ( fracstep >> 2 );
for( i = 0; i < outwidth; i++ )
{
p2[i] = 4 * ( frac >> 16 );
frac += fracstep;
}
for( i = 0; i < outheight; i++, out += outwidth )
{
inrow = in + inwidth * (int)( ( i + 0.25 ) * inheight / outheight );
inrow2 = in + inwidth * (int)( ( i + 0.75 ) * inheight / outheight );
for( j = 0; j < outwidth; j++ )
{
pix1 = (byte *)inrow + p1[j];
pix2 = (byte *)inrow + p2[j];
pix3 = (byte *)inrow2 + p1[j];
pix4 = (byte *)inrow2 + p2[j];
( ( byte * )( out + j ) )[0] = ( pix1[0] + pix2[0] + pix3[0] + pix4[0] ) >> 2;
( ( byte * )( out + j ) )[1] = ( pix1[1] + pix2[1] + pix3[1] + pix4[1] ) >> 2;
( ( byte * )( out + j ) )[2] = ( pix1[2] + pix2[2] + pix3[2] + pix4[2] ) >> 2;
( ( byte * )( out + j ) )[3] = ( pix1[3] + pix2[3] + pix3[3] + pix4[3] ) >> 2;
}
}
}
/*
================
R_HeightmapToNormalmap
================
*/
static int R_HeightmapToNormalmap( const byte *in, byte *out, int width, int height, float bumpScale )
{
int x, y;
vec3_t n;
float ibumpScale;
const byte *p0, *p1, *p2;
if( !bumpScale )
bumpScale = 1.0f;
bumpScale *= max( 0, r_lighting_bumpscale->value );
ibumpScale = ( 255.0 * 3.0 ) / bumpScale;
memset( out, 255, width * height * 4 );
for( y = 0; y < height; y++ )
{
for( x = 0; x < width; x++, out += 4 )
{
p0 = in + ( y * width + x ) * 4;
p1 = ( x == width - 1 ) ? p0 - x * 4 : p0 + 4;
p2 = ( y == height - 1 ) ? in + x * 4 : p0 + width * 4;
n[0] = ( p0[0] + p0[1] + p0[2] ) - ( p1[0] + p1[1] + p1[2] );
n[1] = ( p2[0] + p2[1] + p2[2] ) - ( p0[0] + p0[1] + p0[2] );
n[2] = ibumpScale;
VectorNormalize( n );
out[0] = ( n[0] + 1 ) * 127.5f;
out[1] = ( n[1] + 1 ) * 127.5f;
out[2] = ( n[2] + 1 ) * 127.5f;
out[3] = ( p0[0] + p0[1] + p0[2] ) / 3;
}
}
return 4;
}
/*
================
R_MergeNormalmapDepthmap
================
*/
static int R_MergeNormalmapDepthmap( const char *pathname, byte *in, int iwidth, int iheight )
{
const char *p;
int width, height, samples;
byte *pic, *pic2;
char *depthName;
size_t depthNameSize;
ENSUREBUFSIZE( imagePathBuf2, strlen( pathname ) + (strlen( "depth" ) + 1) + 5 );
depthName = r_imagePathBuf2;
depthNameSize = r_sizeof_imagePathBuf2;
com.strncpy( depthName, pathname, depthNameSize );
p = com.strstr( pathname, "_norm" );
if( !p )
p = pathname + strlen( pathname );
com.strncpy( depthName + (p - pathname), "_depth", depthNameSize - (p - pathname) );
pic = NULL;
samples = R_LoadImageFromDisk( depthName, depthNameSize, &pic, &width, &height, 1 );
if( pic )
{
if( (width == iwidth) && (height == iheight) )
{
int i;
for( i = iwidth*iheight - 1, pic2 = pic; i > 0; i--, in += 4, pic2 += 4 )
in[3] = ((int)pic2[0] + (int)pic2[1] + (int)pic2[2]) / 3;
return 4;
}
else
{
MsgDev( D_WARN, "different depth map dimensions differ from parent (%s)\n", depthName );
}
}
return 3;
}
/*
================
R_MipMap
Operates in place, quartering the size of the texture
note: if given odd width/height this discards the last row/column of
pixels, rather than doing a proper box-filter scale down (LordHavoc)
================
*/
static void R_MipMap( byte *in, int width, int height )
{
int i, j;
byte *out;
width <<= 2;
height >>= 1;
out = in;
for( i = 0; i < height; i++, in += width )
{
for( j = 0; j < width; j += 8, out += 4, in += 8 )
{
out[0] = ( in[0] + in[4] + in[width+0] + in[width+4] )>>2;
out[1] = ( in[1] + in[5] + in[width+1] + in[width+5] )>>2;
out[2] = ( in[2] + in[6] + in[width+2] + in[width+6] )>>2;
out[3] = ( in[3] + in[7] + in[width+3] + in[width+7] )>>2;
}
}
}
/*
===============
R_TextureFormat
===============
*/
static int R_TextureFormat( int samples, bool noCompress )
{
int bits = r_texturebits->integer;
if( glConfig.ext.texture_compression && !noCompress )
{
if( samples == 3 )
return GL_COMPRESSED_RGB_ARB;
return GL_COMPRESSED_RGBA_ARB;
}
if( samples == 3 )
{
if( bits == 16 )
return GL_RGB5;
else if( bits == 32 )
return GL_RGB8;
return GL_RGB;
}
if( bits == 16 )
return GL_RGBA4;
else if( bits == 32 )
return GL_RGBA8;
return GL_RGBA;
}
/*
===============
R_Upload32
===============
*/
void R_Upload32( byte **data, int width, int height, int flags, int *upload_width, int *upload_height, int *samples, bool subImage )
{
int i, c, comp, format;
int target, target2;
int numTextures;
unsigned *scaled = NULL;
int scaledWidth, scaledHeight;
Com_Assert( samples == NULL );
if( glConfig.ext.texture_non_power_of_two )
{
scaledWidth = width;
scaledHeight = height;
}
else
{
for( scaledWidth = 1; scaledWidth < width; scaledWidth <<= 1 );
for( scaledHeight = 1; scaledHeight < height; scaledHeight <<= 1 );
}
if( flags & IT_SKY )
{
// let people sample down the sky textures for speed
scaledWidth >>= r_skymip->integer;
scaledHeight >>= r_skymip->integer;
}
else if( !( flags & IT_NOPICMIP ) )
{
// let people sample down the world textures for speed
scaledWidth >>= r_picmip->integer;
scaledHeight >>= r_picmip->integer;
}
// don't ever bother with > maxSize textures
if( flags & IT_CUBEMAP )
{
numTextures = 6;
target = GL_TEXTURE_CUBE_MAP_ARB;
target2 = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB;
scaledWidth = bound( 1, scaledWidth, glConfig.maxTextureCubemapSize );
scaledHeight = bound( 1, scaledHeight, glConfig.maxTextureCubemapSize );
}
else
{
numTextures = 1;
target = GL_TEXTURE_2D;
target2 = GL_TEXTURE_2D;
scaledWidth = bound( 1, scaledWidth, glConfig.maxTextureSize );
scaledHeight = bound( 1, scaledHeight, glConfig.maxTextureSize );
}
if( upload_width )
*upload_width = scaledWidth;
if( upload_height )
*upload_height = scaledHeight;
// scan the texture for any non-255 alpha
if( flags & ( IT_NORGB|IT_NOALPHA ) )
{
byte *scan;
if( flags & IT_NORGB )
{
for( i = 0; i < numTextures && data[i]; i++ )
{
scan = ( byte * )data[i];
for( c = width * height; c > 0; c--, scan += 4 )
scan[0] = scan[1] = scan[2] = 255;
}
}
else if( *samples == 4 )
{
for( i = 0; i < numTextures && data[i]; i++ )
{
scan = ( byte * )data[i] + 3;
for( c = width * height; c > 0; c--, scan += 4 )
*scan = 255;
}
*samples = 3;
}
}
if( flags & IT_DEPTH )
{
comp = GL_DEPTH_COMPONENT;
format = GL_DEPTH_COMPONENT;
}
else
{
comp = R_TextureFormat( *samples, flags & IT_NOCOMPRESS );
format = GL_RGBA;
}
if( flags & IT_DEPTH )
{
pglTexParameteri( target, GL_TEXTURE_MIN_FILTER, gl_filter_depth );
pglTexParameteri( target, GL_TEXTURE_MAG_FILTER, gl_filter_depth );
if( glConfig.ext.texture_filter_anisotropic )
pglTexParameteri( target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 );
}
else if( !( flags & IT_NOMIPMAP ) )
{
pglTexParameteri( target, GL_TEXTURE_MIN_FILTER, gl_filter_min );
pglTexParameteri( target, GL_TEXTURE_MAG_FILTER, gl_filter_max );
if( glConfig.ext.texture_filter_anisotropic )
pglTexParameteri( target, GL_TEXTURE_MAX_ANISOTROPY_EXT, glConfig.curTextureFilterAnisotropic );
}
else
{
pglTexParameteri( target, GL_TEXTURE_MIN_FILTER, gl_filter_max );
pglTexParameteri( target, GL_TEXTURE_MAG_FILTER, gl_filter_max );
if( glConfig.ext.texture_filter_anisotropic )
pglTexParameteri( target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 );
}
// clamp if required
if( !( flags & IT_CLAMP ) )
{
pglTexParameteri( target, GL_TEXTURE_WRAP_S, GL_REPEAT );
pglTexParameteri( target, GL_TEXTURE_WRAP_T, GL_REPEAT );
}
else if( glConfig.ext.texture_edge_clamp )
{
pglTexParameteri( target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
pglTexParameteri( target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
}
else
{
pglTexParameteri( target, GL_TEXTURE_WRAP_S, GL_CLAMP );
pglTexParameteri( target, GL_TEXTURE_WRAP_T, GL_CLAMP );
}
if( ( scaledWidth == width ) && ( scaledHeight == height ) && ( flags & IT_NOMIPMAP ) )
{
if( subImage )
{
for( i = 0; i < numTextures; i++, target2++ )
pglTexSubImage2D( target2, 0, 0, 0, scaledWidth, scaledHeight, format, GL_UNSIGNED_BYTE, data[i] );
}
else
{
for( i = 0; i < numTextures; i++, target2++ )
pglTexImage2D( target2, 0, comp, scaledWidth, scaledHeight, 0, format, GL_UNSIGNED_BYTE, data[i] );
}
}
else
{
bool driverMipmap = glConfig.ext.generate_mipmap && !(flags & IT_CUBEMAP);
for( i = 0; i < numTextures; i++, target2++ )
{
unsigned int *mip;
if( scaledWidth == width && scaledHeight == height && driverMipmap )
{
mip = (unsigned *)(data[i]);
}
else
{
if( !scaled )
scaled = ( unsigned * )R_PrepareImageBuffer( TEXTURE_RESAMPLING_BUF, scaledWidth * scaledHeight * 4 );
// resample the texture
mip = scaled;
if( data[i] )
R_ResampleTexture( (unsigned int *)( data[i] ), width, height, mip, scaledWidth, scaledHeight );
else
mip = (unsigned *)NULL;
}
// automatic mipmaps generation
if( !( flags & IT_NOMIPMAP ) && mip && driverMipmap )
pglTexParameteri( target2, GL_GENERATE_MIPMAP_SGIS, GL_TRUE );
if( subImage )
pglTexSubImage2D( target2, 0, 0, 0, scaledWidth, scaledHeight, format, GL_UNSIGNED_BYTE, mip );
else
pglTexImage2D( target2, 0, comp, scaledWidth, scaledHeight, 0, format, GL_UNSIGNED_BYTE, mip );
// mipmaps generation
if( !( flags & IT_NOMIPMAP ) && mip && !driverMipmap )
{
int w, h;
int miplevel = 0;
w = scaledWidth;
h = scaledHeight;
while( w > 1 || h > 1 )
{
R_MipMap( (byte *)mip, w, h );
w >>= 1;
h >>= 1;
if( w < 1 )
w = 1;
if( h < 1 )
h = 1;
miplevel++;
if( subImage )
pglTexSubImage2D( target2, miplevel, 0, 0, w, h, format, GL_UNSIGNED_BYTE, mip );
else
pglTexImage2D( target2, miplevel, comp, w, h, 0, format, GL_UNSIGNED_BYTE, mip );
}
}
}
}
}
/*
===============
R_Upload32_3D
No resampling, scaling, mipmapping. Just to make 3D attenuation work ;)
===============
*/
void R_Upload32_3D_Fast( byte **data, int width, int height, int depth, int flags, int *upload_width, int *upload_height, int *upload_depth, int *samples, bool subImage )
{
int comp;
int scaledWidth, scaledHeight, scaledDepth;
Com_Assert( samples == NULL );
Com_Assert( ( flags & (IT_NOMIPMAP|IT_NOPICMIP) ) == 0 );
for( scaledWidth = 1; scaledWidth < width; scaledWidth <<= 1 ) ;
for( scaledHeight = 1; scaledHeight < height; scaledHeight <<= 1 ) ;
for( scaledDepth = 1; scaledDepth < depth; scaledDepth <<= 1 ) ;
if( width != scaledWidth || height != scaledHeight || depth != scaledDepth )
Host_Error( "R_Upload32_3D: bad texture dimensions (not a power of 2)\n" );
if( scaledWidth > glConfig.maxTextureSize3D || scaledHeight > glConfig.maxTextureSize3D || scaledDepth > glConfig.maxTextureSize3D )
Host_Error( "R_Upload32_3D: texture is too large (resizing is not supported)\n" );
if( upload_width )
*upload_width = scaledWidth;
if( upload_height )
*upload_height = scaledHeight;
if( upload_depth )
*upload_depth = scaledDepth;
comp = R_TextureFormat( *samples, flags & IT_NOCOMPRESS );
if( !( flags & IT_NOMIPMAP ) )
{
pglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, gl_filter_min );
pglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, gl_filter_max );
if( glConfig.ext.texture_filter_anisotropic )
pglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MAX_ANISOTROPY_EXT, glConfig.curTextureFilterAnisotropic );
}
else
{
pglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, gl_filter_max );
pglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, gl_filter_max );
if( glConfig.ext.texture_filter_anisotropic )
pglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 );
}
// clamp if required
if( !( flags & IT_CLAMP ) )
{
pglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT );
pglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT );
pglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT );
}
else if( glConfig.ext.texture_edge_clamp )
{
pglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
pglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
pglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE );
}
else
{
pglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP );
pglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP );
pglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP );
}
if( subImage )
pglTexSubImage3D( GL_TEXTURE_3D, 0, 0, 0, 0, scaledWidth, scaledHeight, scaledDepth, GL_RGBA, GL_UNSIGNED_BYTE, data[0] );
else
pglTexImage3D( GL_TEXTURE_3D, 0, comp, scaledWidth, scaledHeight, scaledDepth, 0, GL_RGBA, GL_UNSIGNED_BYTE, data[0] );
}
/*
================
R_InitPic
================
*/
static _inline image_t *R_InitPic( const char *name, byte **pic, int width, int height, int depth, int flags, int samples )
{
image_t *image;
if( r_numImages == MAX_GLIMAGES )
Host_Error( "R_LoadPic: r_numImages == MAX_GLIMAGES" );
image = images + r_numImages;
image->name = Mem_Alloc( r_texturesPool, strlen( name ) + 1 );
strcpy( image->name, name );
image->width = width;
image->height = height;
image->depth = image->upload_depth = depth;
image->flags = flags;
image->samples = samples;
image->texnum = ++r_numImages;
// add to hash table
if( image_cur_hash >= IMAGES_HASH_SIZE )
image_cur_hash = Com_HashKey( name, IMAGES_HASH_SIZE );
image->hash_next = images_hash[image_cur_hash];
images_hash[image_cur_hash] = image;
pglDeleteTextures( 1, &image->texnum );
GL_Bind( 0, image );
if( depth == 1 )
R_Upload32( pic, width, height, flags, &image->upload_width, &image->upload_height, &image->samples, false );
else
R_Upload32_3D_Fast( pic, width, height, depth, flags, &image->upload_width, &image->upload_height, &image->upload_depth, &image->samples, false );
image_cur_hash = IMAGES_HASH_SIZE+1;
return image;
}
/*
================
R_LoadPic
================
*/
image_t *R_LoadPic( const char *name, byte **pic, int width, int height, int flags, int samples )
{
return R_InitPic( name, pic, width, height, 1, flags, samples );
}
/*
================
R_Load3DPic
================
*/
image_t *R_Load3DPic( const char *name, byte **pic, int width, int height, int depth, int flags, int samples )
{
return R_InitPic( name, pic, width, height, depth, flags, samples );
}
/*
===============
R_FindImage
Finds or loads the given image
===============
*/
image_t *R_FindImage( const char *name, const char *suffix, int flags, float bumpScale )
{
int i, lastDot;
unsigned int len, key;
image_t *image;
int width = 1, height = 1, samples = 0;
char *pathname;
size_t pathsize;
if( !name || !name[0] )
return NULL; // Com_Error (ERR_DROP, "R_FindImage: NULL name");
ENSUREBUFSIZE( imagePathBuf, strlen( name ) + (suffix ? strlen( suffix ) + 1 : 0) + 5 );
pathname = r_imagePathBuf;
pathsize = r_sizeof_imagePathBuf;
lastDot = -1;
for( i = ( name[0] == '/' || name[0] == '\\' ), len = 0; name[i]; i++ )
{
if( name[i] == '.' )
lastDot = len;
if( name[i] == '\\' )
pathname[len++] = '/';
else
pathname[len++] = tolower( name[i] );
}
if( len < 5 )
return NULL;
else if( lastDot != -1 )
len = lastDot;
pathname[len] = 0;
if( suffix )
{
pathname[len++] = '_';
for( i = 0; suffix[i]; i++ )
pathname[len++] = tolower( suffix[i] );
}
// look for it
key = image_cur_hash = Com_HashKey( pathname, IMAGES_HASH_SIZE );
if( flags & IT_HEIGHTMAP )
{
for( image = images_hash[key]; image; image = image->hash_next )
{
if( ( image->flags == flags ) && ( image->bumpScale == bumpScale ) && !strcmp( image->name, pathname ) )
return image;
}
}
else
{
for( image = images_hash[key]; image; image = image->hash_next )
{
if( ( image->flags == flags ) && !strcmp( image->name, pathname ) )
return image;
}
}
pathname[len] = 0;
image = NULL;
//
// load the pic from disk
//
if( flags & IT_CUBEMAP )
{
byte *pic[6];
struct cubemapSufAndFlip
{
char *suf; int flags;
} cubemapSides[2][6] = {
{
{ "px", 0 }, { "nx", 0 }, { "py", 0 },
{ "ny", 0 }, { "pz", 0 }, { "nz", 0 }
},
{
{ "rt", IT_FLIPDIAGONAL }, { "lf", IT_FLIPX|IT_FLIPY|IT_FLIPDIAGONAL }, { "bk", IT_FLIPY },
{ "ft", IT_FLIPX }, { "up", IT_FLIPDIAGONAL }, { "dn", IT_FLIPDIAGONAL }
}
};
int j, lastSize = 0;
pathname[len] = '_';
for( i = 0; i < 2; i++ )
{
for( j = 0; j < 6; j++ )
{
pathname[len+1] = cubemapSides[i][j].suf[0];
pathname[len+2] = cubemapSides[i][j].suf[1];
pathname[len+3] = 0;
samples = R_LoadImageFromDisk( pathname, pathsize, &(pic[j]), &width, &height, j );
if( pic[j] )
{
if( width != height )
{
Msg( "Not square cubemap image %s\n", pathname );
break;
}
if( !j )
{
lastSize = width;
}
else if( lastSize != width )
{
Msg( "Different cubemap image size: %s\n", pathname );
break;
}
if( cubemapSides[i][j].flags & ( IT_FLIPX|IT_FLIPY|IT_FLIPDIAGONAL ) )
{
int flags = cubemapSides[i][j].flags;
byte *temp = R_PrepareImageBuffer( TEXTURE_FLIPPING_BUF0+j, width * height * 4 );
R_FlipTexture( pic[j], temp, width, height, 4, (flags & IT_FLIPX), (flags & IT_FLIPY), (flags & IT_FLIPDIAGONAL) );
pic[j] = temp;
}
continue;
}
break;
}
if( j == 6 )
break;
}
if( i != 2 )
{
pathname[len] = 0;
image = R_LoadPic( pathname, pic, width, height, flags, samples );
image->extension[0] = '.';
com.strncpy( &image->extension[1], &pathname[len+4], sizeof( image->extension )-1 );
}
}
else
{
byte *pic;
samples = R_LoadImageFromDisk( pathname, pathsize, &pic, &width, &height, 0 );
if( pic )
{
byte *temp;
if( flags & IT_NORMALMAP )
{
if( (samples == 3) && glConfig.ext.GLSL )
samples = R_MergeNormalmapDepthmap( pathname, pic, width, height );
}
else if( flags & IT_HEIGHTMAP )
{
temp = R_PrepareImageBuffer( TEXTURE_FLIPPING_BUF0, width * height * 4 );
samples = R_HeightmapToNormalmap( pic, temp, width, height, bumpScale );
pic = temp;
}
if( flags & ( IT_FLIPX|IT_FLIPY|IT_FLIPDIAGONAL ) )
{
temp = R_PrepareImageBuffer( TEXTURE_FLIPPING_BUF0, width * height * 4 );
R_FlipTexture( pic, temp, width, height, 4, (flags & IT_FLIPX), (flags & IT_FLIPY), (flags & IT_FLIPDIAGONAL) );
pic = temp;
}
image = R_LoadPic( pathname, &pic, width, height, flags, samples );
image->extension[0] = '.';
com.strncpy( &image->extension[1], &pathname[len+1], sizeof( image->extension )-1 );
}
}
return image;
}
/*
==============================================================================
SCREEN SHOTS
==============================================================================
*/
bool VID_ScreenShot( const char *filename, bool levelshot )
{
rgbdata_t *r_shot;
uint flags = IMAGE_FLIP_Y;
bool result;
r_shot = Mem_Alloc( r_temppool, sizeof( rgbdata_t ));
r_shot->width = glState.width;
r_shot->height = glState.height;
r_shot->type = PF_RGB_24;
r_shot->size = r_shot->width * r_shot->height * PFDesc( r_shot->type )->bpp;
r_shot->palette = NULL;
r_shot->numLayers = 1;
r_shot->numMips = 1;
r_shot->buffer = Mem_Alloc( r_temppool, glState.width * glState.height * 3 );
// get screen frame
pglReadPixels( 0, 0, glState.width, glState.height, GL_RGB, GL_UNSIGNED_BYTE, r_shot->buffer );
if( levelshot ) flags |= IMAGE_RESAMPLE;
// else VID_ImageAdjustGamma( r_shot->buffer, r_shot->width, r_shot->height ); // adjust brightness
Image_Process( &r_shot, 512, 384, flags );
// write image
result = FS_SaveImage( filename, r_shot );
FS_FreeImage( r_shot );
return result;
}
/*
=================
VID_CubemapShot
=================
*/
bool VID_CubemapShot( const char *base, uint size, bool skyshot )
{
rgbdata_t *r_shot;
byte *temp = NULL;
byte *buffer = NULL;
string basename;
int i = 1, result;
if( RI.refdef.onlyClientDraw || !r_worldmodel )
return false;
// make sure the specified size is valid
while( i < size ) i<<=1;
if( i != size ) return false;
if( size > glState.width || size > glState.height )
return false;
// setup refdef
RI.params |= RP_ENVVIEW; // do not render non-bmodel entities
// alloc space
temp = Mem_Alloc( r_temppool, size * size * 3 );
buffer = Mem_Alloc( r_temppool, size * size * 3 * 6 );
for( i = 0; i < 6; i++ )
{
if( skyshot ) R_DrawCubemapView( r_lastRefdef.vieworg, r_skyBoxAngles[i], size );
else R_DrawCubemapView( r_lastRefdef.vieworg, r_cubeMapAngles[i], size );
pglReadPixels( 0, glState.height - size, size, size, GL_RGB, GL_UNSIGNED_BYTE, temp );
Mem_Copy( buffer + (size * size * 3 * i), temp, size * size * 3 );
}
RI.params &= ~RP_ENVVIEW;
r_shot = Mem_Alloc( r_temppool, sizeof( rgbdata_t ));
r_shot->flags |= IMAGE_CUBEMAP;
r_shot->width = size;
r_shot->height = size;
r_shot->type = PF_RGB_24;
r_shot->size = r_shot->width * r_shot->height * 3 * 6;
r_shot->palette = NULL;
r_shot->numLayers = 1;
r_shot->numMips = 1;
r_shot->buffer = buffer;
// make sure what we have right extension
com.strncpy( basename, base, MAX_STRING );
FS_StripExtension( basename );
FS_DefaultExtension( basename, ".dds" );
// write image as dds packet
result = FS_SaveImage( basename, r_shot );
FS_FreeImage( r_shot );
Mem_Free( temp );
return result;
}
//=======================================================
/*
==================
R_InitNoTexture
==================
*/
static byte *R_InitNoTexture( int *w, int *h, int *depth, int *flags, int *samples )
{
int x, y;
byte *data;
byte dottexture[8][8] =
{
{ 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 1, 1, 1, 1, 0, 0, 0 },
{ 0, 1, 1, 1, 1, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0 },
};
//
// also use this for bad textures, but without alpha
//
*w = *h = 8;
*depth = 1;
*flags = 0;
*samples = 3;
data = R_PrepareImageBuffer( TEXTURE_LOADING_BUF0, 8 * 8 * 4 );
for( x = 0; x < 8; x++ )
{
for( y = 0; y < 8; y++ )
{
data[( y*8 + x )*4+0] = dottexture[x&3][y&3]*127;
data[( y*8 + x )*4+1] = dottexture[x&3][y&3]*127;
data[( y*8 + x )*4+2] = dottexture[x&3][y&3]*127;
}
}
return data;
}
/*
==================
R_InitDynamicLightTexture
==================
*/
static byte *R_InitDynamicLightTexture( int *w, int *h, int *depth, int *flags, int *samples )
{
vec3_t v = { 0, 0, 0 };
float intensity;
int x, y, z, d, size;
byte *data;
//
// dynamic light texture
//
if( glConfig.ext.texture3D )
{
size = 32;
*depth = size;
}
else
{
size = 64;
*depth = 1;
}
*w = *h = size;
*flags = IT_NOPICMIP|IT_NOMIPMAP|IT_CLAMP|IT_NOCOMPRESS;
*samples = 3;
data = R_PrepareImageBuffer( TEXTURE_LOADING_BUF0, size * size * *depth * 4 );
for( x = 0; x < size; x++ )
{
for( y = 0; y < size; y++ )
{
for( z = 0; z < *depth; z++ )
{
v[0] = ( ( x + 0.5f ) * ( 2.0f / (float)size ) - 1.0f );
v[1] = ( ( y + 0.5f ) * ( 2.0f / (float)size ) - 1.0f );
if( *depth > 1 )
v[2] = ( ( z + 0.5f ) * ( 2.0f / (float)size ) - 1.0f );
intensity = 1.0f - sqrt( DotProduct( v, v ) );
if( intensity > 0 )
intensity = intensity * intensity * 215.5f;
d = bound( 0, intensity, 255 );
data[( ( z*size+y )*size + x ) * 4 + 0] = d;
data[( ( z*size+y )*size + x ) * 4 + 1] = d;
data[( ( z*size+y )*size + x ) * 4 + 2] = d;
}
}
}
return data;
}
/*
==================
R_InitSolidColorTexture
==================
*/
static byte *R_InitSolidColorTexture( int *w, int *h, int *depth, int *flags, int *samples, int color )
{
byte *data;
//
// solid color texture
//
*w = *h = 1;
*depth = 1;
*flags = IT_NOPICMIP|IT_NOCOMPRESS;
*samples = 3;
data = R_PrepareImageBuffer( TEXTURE_LOADING_BUF0, 1 * 1 * 4 );
data[0] = data[1] = data[2] = color;
return data;
}
/*
==================
R_InitParticleTexture
==================
*/
static byte *R_InitParticleTexture( int *w, int *h, int *depth, int *flags, int *samples )
{
int x, y;
int dx2, dy, d;
byte *data;
//
// particle texture
//
*w = *h = 16;
*depth = 1;
*flags = IT_NOPICMIP|IT_NOMIPMAP;
*samples = 4;
data = R_PrepareImageBuffer( TEXTURE_LOADING_BUF0, 16 * 16 * 4 );
for( x = 0; x < 16; x++ )
{
dx2 = x - 8;
dx2 = dx2 * dx2;
for( y = 0; y < 16; y++ )
{
dy = y - 8;
d = 255 - 35 *sqrt( dx2 + dy *dy );
data[( y*16 + x ) * 4 + 3] = bound( 0, d, 255 );
}
}
return data;
}
/*
==================
R_InitWhiteTexture
==================
*/
static byte *R_InitWhiteTexture( int *w, int *h, int *depth, int *flags, int *samples )
{
return R_InitSolidColorTexture( w, h, depth, flags, samples, 255 );
}
/*
==================
R_InitBlackTexture
==================
*/
static byte *R_InitBlackTexture( int *w, int *h, int *depth, int *flags, int *samples )
{
return R_InitSolidColorTexture( w, h, depth, flags, samples, 0 );
}
/*
==================
R_InitBlankBumpTexture
==================
*/
static byte *R_InitBlankBumpTexture( int *w, int *h, int *depth, int *flags, int *samples )
{
byte *data = R_InitSolidColorTexture( w, h, depth, flags, samples, 128 );
/*
data[2] = 128; // normal X
data[1] = 128; // normal Y
*/
data[0] = 255; // normal Z
data[3] = 128; // height
return data;
}
/*
==================
R_InitFogTexture
==================
*/
static byte *R_InitFogTexture( int *w, int *h, int *depth, int *flags, int *samples )
{
byte *data;
int x, y;
double tw = 1.0f / ( (float)FOG_TEXTURE_WIDTH - 1.0f );
double th = 1.0f / ( (float)FOG_TEXTURE_HEIGHT - 1.0f );
double tx, ty, t;
//
// fog texture
//
*w = FOG_TEXTURE_WIDTH;
*h = FOG_TEXTURE_HEIGHT;
*depth = 1;
*flags = IT_NOMIPMAP|IT_CLAMP;
*samples = 4;
data = R_PrepareImageBuffer( TEXTURE_LOADING_BUF0, FOG_TEXTURE_WIDTH*FOG_TEXTURE_HEIGHT*4 );
for( y = 0, ty = 0.0f; y < FOG_TEXTURE_HEIGHT; y++, ty += th )
{
for( x = 0, tx = 0.0f; x < FOG_TEXTURE_WIDTH; x++, tx += tw )
{
t = sqrt( tx ) * 255.0;
data[( x+y*FOG_TEXTURE_WIDTH )*4+3] = (byte)( min( t, 255.0 ) );
}
data[y*4+3] = 0;
}
return data;
}
/*
==================
R_InitCoronaTexture
==================
*/
static byte *R_InitCoronaTexture( int *w, int *h, int *depth, int *flags, int *samples )
{
int x, y, a;
float dx, dy;
byte *data;
//
// light corona texture
//
*w = *h = 32;
*depth = 1;
*flags = IT_NOMIPMAP|IT_NOPICMIP|IT_NOCOMPRESS|IT_CLAMP;
*samples = 4;
data = R_PrepareImageBuffer( TEXTURE_LOADING_BUF0, 32 * 32 * 4 );
for( y = 0; y < 32; y++ )
{
dy = ( y - 15.5f ) * ( 1.0f / 16.0f );
for( x = 0; x < 32; x++ )
{
dx = ( x - 15.5f ) * ( 1.0f / 16.0f );
a = (int)( ( ( 1.0f / ( dx * dx + dy * dy + 0.2f ) ) - ( 1.0f / ( 1.0f + 0.2 ) ) ) * 32.0f / ( 1.0f / ( 1.0f + 0.2 ) ) );
a = bound( 0, a, 255 );
data[( y*32+x )*4+0] = data[( y*32+x )*4+1] = data[( y*32+x )*4+2] = a;
}
}
return data;
}
/*
==================
R_InitScreenTexture
==================
*/
static void R_InitScreenTexture( image_t **texture, const char *name, int id, int screenWidth, int screenHeight, int size, int flags, int samples )
{
int limit;
int width, height;
limit = glConfig.maxTextureSize;
if( size )
limit = min( limit, size );
if( glConfig.ext.texture_non_power_of_two ) {
width = min( screenWidth, limit );
height = min( screenHeight, limit );
} else {
limit = min( limit, min( screenWidth, screenHeight ) );
for( size = 2; size <= limit; size <<= 1 );
width = height = size >> 1;
}
if( !( *texture ) || ( *texture )->width != width || ( *texture )->height != height )
{
byte *data = NULL;
if( !*texture )
{
char uploadName[128];
com.snprintf( uploadName, sizeof( uploadName ), "***%s%i***", name, id );
*texture = R_LoadPic( uploadName, &data, width, height, flags, samples );
return;
}
GL_Bind( 0, *texture );
( *texture )->width = width;
( *texture )->height = height;
R_Upload32( &data, width, height, flags, &( ( *texture )->upload_width ), &( ( *texture )->upload_height ),
&( ( *texture )->samples ), false );
}
}
/*
==================
R_InitPortalTexture
==================
*/
void R_InitPortalTexture( image_t **texture, int id, int screenWidth, int screenHeight )
{
R_InitScreenTexture( texture, "r_portaltexture", id, screenWidth, screenHeight, r_portalmaps_maxtexsize->integer, IT_PORTALMAP, 3 );
}
/*
==================
R_InitShadowmapTexture
==================
*/
void R_InitShadowmapTexture( image_t **texture, int id, int screenWidth, int screenHeight )
{
R_InitScreenTexture( texture, "r_shadowmap", id, screenWidth, screenHeight, r_shadows_maxtexsize->integer, IT_SHADOWMAP, 1 );
}
/*
==================
R_InitCinematicTexture
==================
*/
static void R_InitCinematicTexture( void )
{
// reserve a dummy texture slot
r_cintexture = &images[r_numImages++];
r_cintexture->texnum = 1;
r_cintexture->depth = 1;
}
/*
==================
R_InitBuiltinTextures
==================
*/
static void R_InitBuiltinTextures( void )
{
byte *data;
int w, h, depth, flags, samples;
image_t *image;
const struct
{
char *name;
image_t **image;
byte *( *init )( int *w, int *h, int *depth, int *flags, int *samples );
}
textures[] =
{
{ "***r_notexture***", &r_notexture, R_InitNoTexture },
{ "***r_whitetexture***", &r_whitetexture, R_InitWhiteTexture },
{ "***r_blacktexture***", &r_blacktexture, R_InitBlackTexture },
{ "***r_blankbumptexture***", &r_blankbumptexture, R_InitBlankBumpTexture },
{ "***r_dlighttexture***", &r_dlighttexture, R_InitDynamicLightTexture },
{ "***r_particletexture***", &r_particletexture, R_InitParticleTexture },
{ "***r_fogtexture***", &r_fogtexture, R_InitFogTexture },
{ "***r_coronatexture***", &r_coronatexture, R_InitCoronaTexture },
{ NULL, NULL, NULL }
};
size_t i, num_builtin_textures = sizeof( textures ) / sizeof( textures[0] ) - 1;
for( i = 0; i < num_builtin_textures; i++ )
{
data = textures[i].init( &w, &h, &depth, &flags, &samples );
Com_Assert( data == NULL );
image = ( depth == 1 ?
R_LoadPic( textures[i].name, &data, w, h, flags, samples ) :
R_Load3DPic( textures[i].name, &data, w, h, depth, flags, samples )
);
if( textures[i].image )
*( textures[i].image ) = image;
}
r_portaltexture = NULL;
r_portaltexture2 = NULL;
}
//=======================================================
/*
===============
R_InitImages
===============
*/
void R_InitImages( void )
{
r_texturesPool = Mem_AllocPool( "Textures" );
image_cur_hash = IMAGES_HASH_SIZE+1;
r_imagePathBuf = r_imagePathBuf2 = NULL;
r_sizeof_imagePathBuf = r_sizeof_imagePathBuf2 = 0;
R_InitCinematicTexture();
R_InitBuiltinTextures();
R_InitBloomTextures();
}
/*
===============
R_ShutdownImages
===============
*/
void R_ShutdownImages( void )
{
int i;
if( !r_texturesPool )
return;
R_FreeImageBuffers ();
for( i = 0; i < r_numImages; i++ )
if( images[i].name )
Mem_Free( images[i].name );
if( r_imagePathBuf )
Mem_Free( r_imagePathBuf );
if( r_imagePathBuf2 )
Mem_Free( r_imagePathBuf2 );
Mem_FreePool( &r_texturesPool );
r_portaltexture = NULL;
r_portaltexture2 = NULL;
memset( r_shadowmapTextures, 0, sizeof( image_t * ) * MAX_SHADOWGROUPS );
r_imagePathBuf = r_imagePathBuf2 = NULL;
r_sizeof_imagePathBuf = r_sizeof_imagePathBuf2 = 0;
r_numImages = 0;
memset( images, 0, sizeof( images ) );
memset( r_lightmapTextures, 0, sizeof( r_lightmapTextures ) );
memset( images_hash, 0, sizeof( images_hash ) );
}