summaryrefslogtreecommitdiffstats
path: root/warsow/r_image.c.new
diff options
context:
space:
mode:
Diffstat (limited to 'warsow/r_image.c.new')
-rw-r--r--warsow/r_image.c.new2967
1 files changed, 2967 insertions, 0 deletions
diff --git a/warsow/r_image.c.new b/warsow/r_image.c.new
new file mode 100644
index 0000000..84d6081
--- /dev/null
+++ b/warsow/r_image.c.new
@@ -0,0 +1,2967 @@
+/*
+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"
+
+#if defined ( __MACOSX__ )
+#include "libjpeg/jpeglib.h"
+#else
+#include "jpeglib.h"
+#endif
+
+#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 int *r_8to24table;
+
+static mempool_t *r_texturesPool;
+static char *r_imagePathBuf, *r_imagePathBuf2;
+static size_t r_sizeof_imagePathBuf, r_sizeof_imagePathBuf2;
+
+#undef ENSUREBUFSIZE
+#define ENSUREBUFSIZE(buf,need) \
+ if( r_sizeof_ ##buf < need ) \
+ { \
+ if( r_ ##buf ) \
+ Mem_Free( r_ ##buf ); \
+ r_sizeof_ ##buf += (((need) & (MAX_QPATH-1))+1) * MAX_QPATH; \
+ 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;
+
+int gl_anisotropic_filter = 0;
+
+void GL_SelectTexture( int tmu )
+{
+ if( !glConfig.ext.multitexture )
+ return;
+ if( tmu == glState.currentTMU )
+ return;
+
+ glState.currentTMU = tmu;
+
+ if( qglActiveTextureARB )
+ {
+ qglActiveTextureARB( tmu + GL_TEXTURE0_ARB );
+ qglClientActiveTextureARB( tmu + GL_TEXTURE0_ARB );
+ }
+ else if( qglSelectTextureSGIS )
+ {
+ qglSelectTextureSGIS( tmu + GL_TEXTURE0_SGIS );
+ }
+}
+
+void GL_TexEnv( GLenum mode )
+{
+ if( mode != ( GLenum )glState.currentEnvModes[glState.currentTMU] )
+ {
+ qglTexEnvi( 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 )
+ qglBindTexture( GL_TEXTURE_CUBE_MAP_ARB, tex->texnum );
+ else if( tex->depth != 1 )
+ qglBindTexture( GL_TEXTURE_3D, tex->texnum );
+ else
+ qglBindTexture( GL_TEXTURE_2D, tex->texnum );
+}
+
+void GL_LoadTexMatrix( const mat4x4_t m )
+{
+ qglMatrixMode( GL_TEXTURE );
+ qglLoadMatrixf( m );
+ glState.texIdentityMatrix[glState.currentTMU] = qfalse;
+}
+
+void GL_LoadIdentityTexMatrix( void )
+{
+ if( !glState.texIdentityMatrix[glState.currentTMU] )
+ {
+ qglMatrixMode( GL_TEXTURE );
+ qglLoadIdentity();
+ glState.texIdentityMatrix[glState.currentTMU] = qtrue;
+ }
+}
+
+void GL_EnableTexGen( int coord, int mode )
+{
+ int tmu = glState.currentTMU;
+ int bit, gen;
+
+ bit = 1 << (coord - GL_S);
+ gen = GL_TEXTURE_GEN_S + (coord - GL_S);
+
+ assert( gen == bound( GL_TEXTURE_GEN_S, gen, GL_TEXTURE_GEN_Q ) );
+
+ if( mode )
+ {
+ if( !( glState.genSTEnabled[tmu] & bit ) )
+ {
+ qglEnable( gen );
+ glState.genSTEnabled[tmu] |= bit;
+ }
+ qglTexGeni( coord, GL_TEXTURE_GEN_MODE, mode );
+ }
+ else
+ {
+ if( glState.genSTEnabled[tmu] & bit )
+ {
+ qglDisable( gen );
+ glState.genSTEnabled[tmu] &= ~bit;
+ }
+ }
+}
+
+void GL_SetTexCoordArrayMode( int mode )
+{
+ int tmu = glState.currentTMU;
+ int cmode = glState.texCoordArrayMode[tmu];
+
+ if( cmode != mode )
+ {
+ if( cmode == GL_TEXTURE_COORD_ARRAY )
+ qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
+ else if( cmode == GL_TEXTURE_CUBE_MAP_ARB )
+ qglDisable( GL_TEXTURE_CUBE_MAP_ARB );
+
+ if( mode == GL_TEXTURE_COORD_ARRAY )
+ qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
+ else if( mode == GL_TEXTURE_CUBE_MAP_ARB )
+ qglEnable( GL_TEXTURE_CUBE_MAP_ARB );
+
+ glState.texCoordArrayMode[tmu] = mode;
+ }
+}
+
+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( !Q_stricmp( modes[i].name, string ) )
+ break;
+ }
+
+ if( i == NUM_GL_MODES )
+ {
+ Com_Printf( "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++ )
+ {
+ if( glt->flags & (IT_NOFILTERING|IT_DEPTH) )
+ continue;
+
+ GL_Bind( 0, glt );
+
+ if( !( glt->flags & IT_NOMIPMAP ) )
+ {
+ qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min );
+ qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max );
+ }
+ else
+ {
+ qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max );
+ qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max );
+ }
+ }
+}
+
+/*
+===============
+R_AnisotropicFilter=
+===============
+*/
+void R_AnisotropicFilter( int value )
+{
+ int i, old;
+ image_t *glt;
+
+ if( !glConfig.ext.texture_filter_anisotropic )
+ return;
+
+ old = gl_anisotropic_filter;
+ gl_anisotropic_filter = bound( 1, value, glConfig.maxTextureFilterAnisotropic );
+ if( gl_anisotropic_filter == old )
+ return;
+
+ // change all the existing mipmap texture objects
+ for( i = 1, glt = images; i < r_numImages; i++, glt++ )
+ {
+ if( (glt->flags & (IT_NOFILTERING|IT_DEPTH|IT_NOMIPMAP)) )
+ continue;
+
+ GL_Bind( 0, glt );
+ if( glt->upload_depth != 1 )
+ qglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_anisotropic_filter );
+ else
+ qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_anisotropic_filter );
+ }
+}
+
+/*
+===============
+R_ImageList_f
+===============
+*/
+void R_ImageList_f( void )
+{
+ int i, num_depth = 0, bytes;
+ image_t *image;
+ double texels = 0, add, total_bytes = 0;
+ double depth_texels = 0;
+
+ Com_Printf( "------------------\n" );
+
+ for( i = 0, image = images; i < r_numImages; i++, image++ )
+ {
+ if( !image->upload_width || !image->upload_height || !image->upload_depth )
+ continue;
+
+ add = image->upload_width * image->upload_height * image->upload_depth;
+ if( !(image->flags & (IT_DEPTH|IT_NOFILTERING|IT_NOMIPMAP)) )
+ add = (unsigned)floor( add / 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;
+ Com_Printf( " %4i %4i %4i: %s\n", image->upload_width, image->upload_height, image->upload_depth,
+ image->name );
+ }
+ else
+ {
+ texels += add;
+ bytes = add * (image->flags & IT_LUMINANCE ? 1 : 4);
+ total_bytes += bytes;
+
+ Com_Printf( " %4i %4i%s: %s%s%s%s %.1f KB\n", image->upload_width, image->upload_height,
+ image->upload_depth > 1 ? va( " %4i", image->upload_depth ) : "",
+ (image->flags & IT_NORGB ? "*A" : (image->flags & IT_NOALPHA ? "*C" : "")),
+ image->name, image->extension, ((image->flags & (IT_NOMIPMAP|IT_NOFILTERING)) ? "" : " (mip)"), bytes / 1024.0 );
+ }
+ }
+
+ Com_Printf( "Total texels count (counting mipmaps, approx): %.0f\n", texels );
+ Com_Printf( "%i RGBA images, totalling %.3f megabytes\n", r_numImages - 1, total_bytes / 1048576.0 );
+ if( num_depth )
+ Com_Printf( "%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_CUT_BUF,
+ TEXTURE_FLIPPING_BUF0,TEXTURE_FLIPPING_BUF1,TEXTURE_FLIPPING_BUF2,TEXTURE_FLIPPING_BUF3,TEXTURE_FLIPPING_BUF4,TEXTURE_FLIPPING_BUF5,
+
+ NUM_IMAGE_BUFFERS
+};
+
+static qbyte *r_aviBuffer;
+
+static qbyte *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 qbyte *_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, 0, 0, filename, fileline );
+ }
+
+ 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;
+ }
+}
+
+/*
+=================================================================
+
+PCX LOADING
+
+=================================================================
+*/
+
+typedef struct
+{
+ char manufacturer;
+ char version;
+ char encoding;
+ char bits_per_pixel;
+ unsigned short xmin, ymin, xmax, ymax;
+ unsigned short hres, vres;
+ unsigned char palette[48];
+ char reserved;
+ char color_planes;
+ unsigned short bytes_per_line;
+ unsigned short palette_type;
+ char filler[58];
+ unsigned char data; // unbounded
+} pcx_t;
+
+static qbyte pcx_pal[768];
+
+/*
+==============
+LoadPCX
+==============
+*/
+static int LoadPCX( const char *filename, qbyte **pic, int *width, int *height, int side )
+{
+ qbyte *raw;
+ pcx_t *pcx;
+ int x, y, samples = 3;
+ int len, columns, rows;
+ int dataByte, runLength;
+ qbyte *pal = pcx_pal, *pix;
+ qbyte stack[0x4000];
+
+ *pic = NULL;
+
+ //
+ // load the file
+ //
+ len = FS_LoadFile( filename, (void **)&raw, stack, sizeof( stack ) );
+ if( !raw )
+ return 0;
+
+ //
+ // parse the PCX file
+ //
+ pcx = (pcx_t *)raw;
+
+ pcx->xmin = LittleShort( pcx->xmin );
+ pcx->ymin = LittleShort( pcx->ymin );
+ pcx->xmax = LittleShort( pcx->xmax );
+ pcx->ymax = LittleShort( pcx->ymax );
+ pcx->hres = LittleShort( pcx->hres );
+ pcx->vres = LittleShort( pcx->vres );
+ pcx->bytes_per_line = LittleShort( pcx->bytes_per_line );
+ pcx->palette_type = LittleShort( pcx->palette_type );
+
+ raw = &pcx->data;
+
+ if( pcx->manufacturer != 0x0a
+ || pcx->version != 5
+ || pcx->encoding != 1
+ || pcx->bits_per_pixel != 8 )
+ {
+ Com_DPrintf( S_COLOR_YELLOW "Bad pcx file %s\n", filename );
+ if( ( qbyte *)pcx != stack )
+ FS_FreeFile( pcx );
+ return 0;
+ }
+
+ columns = pcx->xmax + 1;
+ rows = pcx->ymax + 1;
+ pix = *pic = R_PrepareImageBuffer( TEXTURE_LOADING_BUF0+side, columns * rows * 4 );
+ memcpy( pal, (qbyte *)pcx + len - 768, 768 );
+
+ if( width )
+ *width = columns;
+ if( height )
+ *height = rows;
+
+ for( y = 0; y < rows; y++ )
+ {
+ for( x = 0; x < columns; )
+ {
+ dataByte = *raw++;
+
+ if( ( dataByte & 0xC0 ) == 0xC0 )
+ {
+ runLength = dataByte & 0x3F;
+ dataByte = *raw++;
+ }
+ else
+ runLength = 1;
+
+ while( runLength-- > 0 )
+ {
+#ifdef QUAKE2_JUNK
+ if( dataByte == 255 )
+ { // hack Quake2 palette
+ pix[0] = 0;
+ pix[1] = 0;
+ pix[2] = 0;
+ pix[3] = 0;
+ samples = 4;
+ }
+ else
+#endif
+ {
+ pix[0] = pal[dataByte*3+0];
+ pix[1] = pal[dataByte*3+1];
+ pix[2] = pal[dataByte*3+2];
+ pix[3] = 255;
+ }
+ x++; pix += 4;
+ }
+ }
+ }
+
+ if( raw - (qbyte *)pcx > len )
+ {
+ Com_DPrintf( S_COLOR_YELLOW "PCX file %s was malformed", filename );
+ *pic = NULL;
+ }
+
+ if( (qbyte *)pcx != stack )
+ FS_FreeFile( pcx );
+
+ return samples;
+}
+
+/*
+===============
+R_GetQ1Palette
+===============
+*/
+static int *R_GetQ1Palette( void )
+{
+ qbyte *raw;
+ int r, g, b, v;
+ int i, len;
+ qbyte stack[0x4000];
+ int *out;
+ const qbyte *pal;
+ static const qbyte host_quakepal[768] =
+#include "../qcommon/quake1pal.h"
+ ;
+
+ // get the palette
+ len = FS_LoadFile( "gfx/palette.lmp", (void **)&raw, stack, sizeof( stack ) );
+ pal = ( raw && len >= 768 ) ? raw : host_quakepal;
+ out = Mem_Alloc( r_texturesPool, sizeof( *out ) * 256 );
+
+ for( i = 0; i < 256; i++ )
+ {
+ r = pal[i*3 + 0];
+ g = pal[i*3 + 1];
+ b = pal[i*3 + 2];
+
+ v = COLOR_RGBA( r, g, b, 255 );
+ out[i] = LittleLong( v );
+ }
+
+ if( (qbyte *)raw != stack )
+ FS_FreeFile( raw );
+
+ out[255] = 0; // 255 is transparent
+
+ return out;
+}
+
+/*
+===============
+R_GetQ2Palette
+===============
+*/
+static int *R_GetQ2Palette( void )
+{
+ int i;
+ int *out;
+ qbyte *pic, *pal = pcx_pal;
+ int r, g, b;
+ unsigned v;
+ int width, height;
+
+ // get the palette
+ LoadPCX( "pics/colormap.pcx", &pic, &width, &height, 0 );
+ if( !pic )
+ return NULL;
+
+ out = Mem_Alloc( r_texturesPool, sizeof( *out ) * 256 );
+ for( i = 0; i < 256; i++ )
+ {
+ r = pal[i*3 + 0];
+ g = pal[i*3 + 1];
+ b = pal[i*3 + 2];
+
+ v = COLOR_RGBA( r, g, b, 255 );
+ out[i] = LittleLong( v );
+ }
+
+ out[255] &= LittleLong( 0xffffff ); // 255 is transparent
+ return out;
+}
+
+/*
+===============
+R_GetPalette
+
+Loads Q1 or Q2 palette from disk if not already loaded
+===============
+*/
+static int *R_GetPalette( int flags )
+{
+ int i;
+
+ if( !r_8to24table )
+ {
+ if( flags & IT_MIPTEX )
+ r_8to24table = R_GetQ1Palette();
+ else if( flags & IT_WAL )
+ r_8to24table = R_GetQ2Palette();
+
+ if( !r_8to24table )
+ {
+ r_8to24table = Mem_Alloc( r_texturesPool, sizeof( *r_8to24table ) * 256 );
+
+ // whatever...
+ for( i = 0; i < 256; i++ )
+ r_8to24table[i] = LittleLong( i );
+ }
+ }
+ return r_8to24table;
+}
+
+/*
+=========================================================
+
+TARGA LOADING
+
+=========================================================
+*/
+
+typedef struct _TargaHeader
+{
+ unsigned char id_length, colormap_type, image_type;
+ unsigned short colormap_index, colormap_length;
+ unsigned char colormap_size;
+ unsigned short x_origin, y_origin, width, height;
+ unsigned char pixel_size, attributes;
+} TargaHeader;
+
+
+/*
+=============
+LoadTGA
+=============
+*/
+static int LoadTGA( const char *name, qbyte **pic, int *width, int *height, int side )
+{
+ int i, columns, rows, row_inc, row, col;
+ qbyte *buf_p, *buffer, *pixbuf, *targa_rgba;
+ int length, samples, readpixelcount, pixelcount;
+ qbyte palette[256][4], red = 0, green = 0, blue = 0, alpha = 0;
+ qboolean compressed;
+ TargaHeader targa_header;
+ qbyte stack[0x4000];
+
+ *pic = NULL;
+
+ //
+ // load the file
+ //
+ length = FS_LoadFile( name, (void **)&buffer, stack, sizeof( stack ) );
+ if( !buffer )
+ return 0;
+
+ buf_p = buffer;
+ targa_header.id_length = *buf_p++;
+ targa_header.colormap_type = *buf_p++;
+ targa_header.image_type = *buf_p++;
+
+ targa_header.colormap_index = buf_p[0] + buf_p[1] * 256;
+ buf_p += 2;
+ targa_header.colormap_length = buf_p[0] + buf_p[1] * 256;
+ buf_p += 2;
+ targa_header.colormap_size = *buf_p++;
+ targa_header.x_origin = LittleShort( *( (short *)buf_p ) );
+ buf_p += 2;
+ targa_header.y_origin = LittleShort( *( (short *)buf_p ) );
+ buf_p += 2;
+ targa_header.width = LittleShort( *( (short *)buf_p ) );
+ buf_p += 2;
+ targa_header.height = LittleShort( *( (short *)buf_p ) );
+ buf_p += 2;
+ targa_header.pixel_size = *buf_p++;
+ targa_header.attributes = *buf_p++;
+ if( targa_header.id_length != 0 )
+ buf_p += targa_header.id_length; // skip TARGA image comment
+
+ if( targa_header.image_type == 1 || targa_header.image_type == 9 )
+ {
+ // uncompressed colormapped image
+ if( targa_header.pixel_size != 8 )
+ {
+ Com_DPrintf( S_COLOR_YELLOW "LoadTGA: Only 8 bit images supported for type 1 and 9" );
+ if( buffer != stack )
+ FS_FreeFile( buffer );
+ return 0;
+ }
+ if( targa_header.colormap_length != 256 )
+ {
+ Com_DPrintf( S_COLOR_YELLOW "LoadTGA: Only 8 bit colormaps are supported for type 1 and 9" );
+ if( buffer != stack )
+ FS_FreeFile( buffer );
+ return 0;
+ }
+ if( targa_header.colormap_index )
+ {
+ Com_DPrintf( S_COLOR_YELLOW "LoadTGA: colormap_index is not supported for type 1 and 9" );
+ if( buffer != stack )
+ FS_FreeFile( buffer );
+ return 0;
+ }
+ if( targa_header.colormap_size == 24 )
+ {
+ for( i = 0; i < targa_header.colormap_length; i++ )
+ {
+ palette[i][2] = *buf_p++;
+ palette[i][1] = *buf_p++;
+ palette[i][0] = *buf_p++;
+ palette[i][3] = 255;
+ }
+ }
+ else if( targa_header.colormap_size == 32 )
+ {
+ for( i = 0; i < targa_header.colormap_length; i++ )
+ {
+ palette[i][2] = *buf_p++;
+ palette[i][1] = *buf_p++;
+ palette[i][0] = *buf_p++;
+ palette[i][3] = *buf_p++;
+ }
+ }
+ else
+ {
+ Com_DPrintf( S_COLOR_YELLOW "LoadTGA: only 24 and 32 bit colormaps are supported for type 1 and 9" );
+ if( buffer != stack )
+ FS_FreeFile( buffer );
+ return 0;
+ }
+ }
+ else if( targa_header.image_type == 2 || targa_header.image_type == 10 )
+ {
+ // uncompressed or RLE compressed RGB
+ if( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 )
+ {
+ Com_DPrintf( S_COLOR_YELLOW "LoadTGA: Only 32 or 24 bit images supported for type 2 and 10" );
+ if( buffer != stack )
+ FS_FreeFile( buffer );
+ return 0;
+ }
+ }
+ else if( targa_header.image_type == 3 || targa_header.image_type == 11 )
+ {
+ // uncompressed grayscale
+ if( targa_header.pixel_size != 8 )
+ {
+ Com_DPrintf( S_COLOR_YELLOW "LoadTGA: Only 8 bit images supported for type 3 and 11" );
+ if( buffer != stack )
+ FS_FreeFile( buffer );
+ return 0;
+ }
+ }
+
+ columns = targa_header.width;
+ if( width )
+ *width = columns;
+
+ rows = targa_header.height;
+ if( height )
+ *height = rows;
+
+ targa_rgba = R_PrepareImageBuffer( TEXTURE_LOADING_BUF0+side, columns * rows * 4 );
+ *pic = targa_rgba;
+
+ // if bit 5 of attributes isn't set, the image has been stored from bottom to top
+ if( targa_header.attributes & 0x20 )
+ {
+ pixbuf = targa_rgba;
+ row_inc = 0;
+ }
+ else
+ {
+ pixbuf = targa_rgba + ( rows - 1 ) * columns * 4;
+ row_inc = -columns * 4 * 2;
+ }
+
+ compressed = ( targa_header.image_type == 9 || targa_header.image_type == 10 || targa_header.image_type == 11 );
+ for( row = col = 0, samples = 3; row < rows; )
+ {
+ pixelcount = 0x10000;
+ readpixelcount = 0x10000;
+
+ if( compressed )
+ {
+ pixelcount = *buf_p++;
+ if( pixelcount & 0x80 ) // run-length packet
+ readpixelcount = 1;
+ pixelcount = 1 + ( pixelcount & 0x7f );
+ }
+
+ while( pixelcount-- && ( row < rows ) )
+ {
+ if( readpixelcount-- > 0 )
+ {
+ switch( targa_header.image_type )
+ {
+ case 1:
+ case 9:
+ // colormapped image
+ blue = *buf_p++;
+ red = palette[blue][0];
+ green = palette[blue][1];
+ alpha = palette[blue][3];
+ blue = palette[blue][2];
+ if( alpha != 255 )
+ samples = 4;
+ break;
+ case 2:
+ case 10:
+ // 24 or 32 bit image
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ alpha = 255;
+ if( targa_header.pixel_size == 32 )
+ {
+ alpha = *buf_p++;
+ if( alpha != 255 )
+ samples = 4;
+ }
+ break;
+ case 3:
+ case 11:
+ // grayscale image
+ blue = green = red = *buf_p++;
+ alpha = 255;
+ break;
+ }
+ }
+
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = alpha;
+ if( ++col == columns )
+ { // run spans across rows
+ row++;
+ col = 0;
+ pixbuf += row_inc;
+ }
+ }
+ }
+
+ if( buffer != stack )
+ FS_FreeFile( buffer );
+
+ return samples;
+}
+
+/*
+==================
+WriteTGA
+==================
+*/
+static qboolean WriteTGA( const char *name, qbyte *buffer, int width, int height, qboolean bgr )
+{
+ int file, i, c, temp;
+
+ if( FS_FOpenFile( name, &file, FS_WRITE ) == -1 )
+ {
+ Com_Printf( "WriteTGA: Couldn't create %s\n", name );
+ return qfalse;
+ }
+
+ buffer[2] = 2; // uncompressed type
+ buffer[12] = width&255;
+ buffer[13] = width>>8;
+ buffer[14] = height&255;
+ buffer[15] = height>>8;
+ buffer[16] = 24; // pixel size
+
+ // swap rgb to bgr
+ c = 18+width*height*3;
+ if( !bgr )
+ {
+ for( i = 18; i < c; i += 3 )
+ {
+ temp = buffer[i];
+ buffer[i] = buffer[i+2];
+ buffer[i+2] = temp;
+ }
+ }
+ FS_Write( buffer, c, file );
+ FS_FCloseFile( file );
+
+ return qtrue;
+}
+
+/*
+=========================================================
+
+JPEG LOADING
+
+=========================================================
+*/
+
+static void jpg_noop( j_decompress_ptr cinfo )
+{
+}
+
+static boolean jpg_fill_input_buffer( j_decompress_ptr cinfo )
+{
+ Com_DPrintf( "Premature end of jpeg file\n" );
+ return 1;
+}
+
+static void jpg_skip_input_data( j_decompress_ptr cinfo, long num_bytes )
+{
+ cinfo->src->next_input_byte += (size_t) num_bytes;
+ cinfo->src->bytes_in_buffer -= (size_t) num_bytes;
+}
+
+/*static void jpeg_mem_src( j_decompress_ptr cinfo, qbyte *mem, int len )
+{
+ cinfo->src = (struct jpeg_source_mgr *)
+ ( *cinfo->mem->alloc_small )( (j_common_ptr) cinfo,
+ JPOOL_PERMANENT,
+ sizeof( struct jpeg_source_mgr ) );
+ cinfo->src->init_source = jpg_noop;
+ cinfo->src->fill_input_buffer = jpg_fill_input_buffer;
+ cinfo->src->skip_input_data = jpg_skip_input_data;
+ cinfo->src->resync_to_restart = jpeg_resync_to_restart;
+ cinfo->src->term_source = jpg_noop;
+ cinfo->src->bytes_in_buffer = len;
+ cinfo->src->next_input_byte = mem;
+}
+*/
+/*
+=============
+LoadJPG
+=============
+*/
+static int LoadJPG( const char *name, qbyte **pic, int *width, int *height, int side )
+{
+ unsigned int i, length, samples, l;
+ qbyte *img, *scan, *buffer, *line;
+ struct jpeg_error_mgr jerr;
+ struct jpeg_decompress_struct cinfo;
+ qbyte stack[0x4000];
+
+ *pic = NULL;
+
+ // load the file
+ length = FS_LoadFile( name, (void **)&buffer, stack, sizeof( stack ) );
+ if( !buffer )
+ return 0;
+
+ cinfo.err = jpeg_std_error( &jerr );
+ jpeg_create_decompress( &cinfo );
+ jpeg_mem_src( &cinfo, buffer, length );
+ jpeg_read_header( &cinfo, TRUE );
+ jpeg_start_decompress( &cinfo );
+ samples = cinfo.output_components;
+
+ if( samples != 3 && samples != 1 )
+ {
+ Com_DPrintf( S_COLOR_YELLOW "Bad jpeg file %s\n", name );
+ jpeg_destroy_decompress( &cinfo );
+ if( buffer != stack )
+ FS_FreeFile( buffer );
+ return 0;
+ }
+
+ if( width )
+ *width = cinfo.output_width;
+ if( height )
+ *height = cinfo.output_height;
+
+ img = *pic = R_PrepareImageBuffer( TEXTURE_LOADING_BUF0+side, cinfo.output_width * cinfo.output_height * 4 );
+ l = cinfo.output_width * samples;
+ if( sizeof( stack ) >= l + length )
+ line = stack + length;
+ else
+ line = R_PrepareImageBuffer( TEXTURE_LINE_BUF, l );
+
+ while( cinfo.output_scanline < cinfo.output_height )
+ {
+ scan = line;
+ if( !jpeg_read_scanlines( &cinfo, &scan, 1 ) )
+ {
+ Com_Printf( S_COLOR_YELLOW "Bad jpeg file %s\n", name );
+ jpeg_destroy_decompress( &cinfo );
+ if( buffer != stack )
+ FS_FreeFile( buffer );
+ return 0;
+ }
+
+ if( samples == 1 )
+ {
+ for( i = 0; i < cinfo.output_width; i++, img += 4 )
+ img[0] = img[1] = img[2] = *scan++;
+ }
+ else
+ {
+ for( i = 0; i < cinfo.output_width; i++, img += 4, scan += 3 )
+ img[0] = scan[0], img[1] = scan[1], img[2] = scan[2];
+ }
+ }
+
+ jpeg_finish_decompress( &cinfo );
+ jpeg_destroy_decompress( &cinfo );
+
+ if( buffer != stack )
+ FS_FreeFile( buffer );
+
+ return 3;
+}
+
+/*
+==================
+WriteJPG
+==================
+*/
+static qboolean WriteJPG( const char *name, qbyte *buffer, int width, int height, int quality )
+{
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ char *fullname;
+ int fullname_size;
+ FILE *f;
+ JSAMPROW s[1];
+ int offset, w3;
+
+ // We can't use FS-functions with libjpeg
+ fullname_size =
+ sizeof( char ) * ( strlen( FS_WriteDirectory() ) + 1 + strlen( FS_GameDirectory() ) + 1 + strlen( name ) + 1 );
+ fullname = Mem_TempMalloc( fullname_size );
+ Q_snprintfz( fullname, fullname_size, "%s/%s/%s", FS_WriteDirectory(), FS_GameDirectory(), name );
+ FS_CreateAbsolutePath( fullname );
+
+ if( !( f = fopen( fullname, "wb" ) ) )
+ {
+ Com_Printf( "WriteJPG: Couldn't create %s\n", fullname );
+ Mem_TempFree( fullname );
+ return qfalse;
+ }
+
+ // initialize the JPEG compression object
+ cinfo.err = jpeg_std_error( &jerr );
+ jpeg_create_compress( &cinfo );
+ jpeg_stdio_dest( &cinfo, f );
+
+ // setup JPEG parameters
+ cinfo.image_width = width;
+ cinfo.image_height = height;
+ cinfo.in_color_space = JCS_RGB;
+ cinfo.input_components = 3;
+
+ jpeg_set_defaults( &cinfo );
+
+ if( ( quality > 100 ) || ( quality <= 0 ) )
+ quality = 85;
+
+ jpeg_set_quality( &cinfo, quality, TRUE );
+
+ // If quality is set high, disable chroma subsampling
+ if( quality >= 85 )
+ {
+ cinfo.comp_info[0].h_samp_factor = 1;
+ cinfo.comp_info[0].v_samp_factor = 1;
+ }
+
+ // start compression
+ jpeg_start_compress( &cinfo, qtrue );
+
+ // feed scanline data
+ w3 = cinfo.image_width * 3;
+ offset = w3 * cinfo.image_height - w3;
+ while( cinfo.next_scanline < cinfo.image_height )
+ {
+ s[0] = &buffer[offset - cinfo.next_scanline * w3];
+ jpeg_write_scanlines( &cinfo, s, 1 );
+ }
+
+ // finish compression
+ jpeg_finish_compress( &cinfo );
+ jpeg_destroy_compress( &cinfo );
+
+ fclose( f );
+ Mem_TempFree( fullname );
+
+ return qtrue;
+}
+
+/*
+=========================================================
+
+MIPTEX LOADING
+
+=========================================================
+*/
+
+/*
+===============
+LoadMipTex
+===============
+*/
+static int LoadMipTex( qbyte **pic, int width, int height, int flags )
+{
+ unsigned int i;
+ int side = 0;
+ unsigned int p, s, *trans;
+ qbyte *data;
+ int *d_8to24table;
+
+ data = *pic;
+
+ // load palette from disk
+ d_8to24table = R_GetPalette( IT_MIPTEX );
+
+ s = width * height;
+ trans = ( unsigned int * )R_PrepareImageBuffer( TEXTURE_LOADING_BUF0+side, s * 4 );
+ *pic = ( qbyte * )trans;
+
+ if( flags & IT_SKY )
+ {
+ unsigned j;
+ unsigned r, g, b, p;
+ unsigned transpix;
+ unsigned rgba;
+ int halfwidth = width >> 1;
+
+ // a sky texture is 256*128, with the right side being a masked overlay
+ r = g = b = 0;
+ for( i = 0; i < (unsigned)height; i++ )
+ {
+ for( j = 0; j < (unsigned)halfwidth; j++ )
+ {
+ p = data[i*width + halfwidth + j];
+ rgba = d_8to24table[p];
+ trans[i*width + halfwidth + j] = rgba;
+ r += COLOR_R( rgba );
+ g += COLOR_G( rgba );
+ b += COLOR_B( rgba );
+ }
+ }
+
+ // make an average value for the back to avoid
+ // a fringe on the top level
+ transpix = COLOR_RGBA( r/(halfwidth*height), g/(halfwidth*height), b/(halfwidth*height), 0 );
+
+ for( i = 0; i < (unsigned)height; i++ )
+ {
+ for( j = 0; j < (unsigned)halfwidth; j++ )
+ {
+ p = data[i*width + j];
+ trans[i*width + j] = p ? d_8to24table[p] : transpix;
+ }
+ }
+ return 4;
+ }
+ else if( flags & IT_MIPTEX_FULLBRIGHT )
+ {
+ // this is a fullbright mask, so make all non-fullbright
+ // colors transparent
+ for( i = 0; i < s; i++ )
+ {
+ p = data[i];
+ if( p < 224 )
+ trans[i] = 0; // transparent
+ else
+ trans[i] = d_8to24table[p]; // fullbright
+ }
+ return 4;
+ }
+ else
+ {
+ for( i = 0; i < s; i++ )
+ {
+ p = data[i];
+ trans[i] = d_8to24table[p];
+ }
+ return 3;
+ }
+}
+
+/*
+===============
+R_MiptexHasFullbrights
+===============
+*/
+qboolean R_MiptexHasFullbrights( qbyte *pixels, int width, int height )
+{
+ int i;
+ int size = width * height;
+
+ for( i = 0; i < size; i++ )
+ {
+ if( pixels[i] >= 224 )
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
+/*
+=========================================================
+
+WAL LOADING
+
+=========================================================
+*/
+
+/*
+===============
+LoadWAL
+===============
+*/
+static int LoadWAL( const char *name, qbyte **pic, int *width, int *height, int side )
+{
+ unsigned int i, length;
+ unsigned int p, s, *trans;
+ unsigned int rows, columns;
+ int samples = 3;
+ qbyte *buffer, *data;
+ q2miptex_t *mt;
+ qbyte stack[0x4000];
+ int *d_8to24table;
+
+ *pic = NULL;
+
+ // load palette from disk
+ d_8to24table = R_GetPalette( IT_WAL );
+
+ // load the file
+ length = FS_LoadFile( name, (void **)&buffer, stack, sizeof( stack ) );
+ if( !buffer )
+ return 0;
+
+ mt = ( q2miptex_t * )buffer;
+ rows = LittleLong( mt->width );
+ columns = LittleLong( mt->height );
+ data = buffer + LittleLong( mt->offsets[0] );
+
+ if( width )
+ *width = ( int )rows;
+ if( height )
+ *height = ( int )columns;
+
+ s = LittleLong( mt->width ) * LittleLong( mt->height );
+ trans = ( unsigned int * )R_PrepareImageBuffer( TEXTURE_LOADING_BUF0+side, s * 4 );
+ *pic = ( qbyte * )trans;
+
+ for( i = 0; i < s ; i++ )
+ {
+ p = data[i];
+ trans[i] = d_8to24table[p];
+
+ if( p == 255 )
+ {
+ // transparent, so scan around for another color
+ // to avoid alpha fringes
+ // FIXME: do a full flood fill so mips work...
+ if( i > rows && data[i-rows] != 255 )
+ p = data[i-rows];
+ else if( i < s-rows && data[i+rows] != 255 )
+ p = data[i+rows];
+ else if( i > 0 && data[i-1] != 255 )
+ p = data[i-1];
+ else if( i < s-1 && data[i+1] != 255 )
+ p = data[i+1];
+ else
+ p = 0;
+
+ // copy rgb components
+ ((qbyte *)&trans[i])[0] = ((qbyte *)&d_8to24table[p])[0];
+ ((qbyte *)&trans[i])[1] = ((qbyte *)&d_8to24table[p])[1];
+ ((qbyte *)&trans[i])[2] = ((qbyte *)&d_8to24table[p])[2];
+
+ samples = 4;
+ }
+ }
+
+ if( ( qbyte * )mt != stack )
+ FS_FreeFile( mt );
+
+ return samples;
+}
+
+//=======================================================
+
+/*
+===============
+R_LoadImageFromDisk
+===============
+*/
+static int R_LoadImageFromDisk( char *pathname, size_t pathname_size, qbyte **pic, int *width, int *height, int *flags, int side )
+{
+ const char *extension;
+
+ *pic = NULL;
+ *width = *height = 0;
+
+ extension = FS_FirstExtension( pathname, IMAGE_EXTENSIONS, NUM_IMAGE_EXTENSIONS );
+ if( extension )
+ {
+ COM_ReplaceExtension( pathname, extension, pathname_size );
+ if( !Q_stricmp( extension, ".jpg" ) )
+ return LoadJPG( pathname, pic, width, height, side );
+ if( !Q_stricmp( extension, ".tga" ) )
+ return LoadTGA( pathname, pic, width, height, side );
+ if( !Q_stricmp( extension, ".pcx" ) )
+ return LoadPCX( pathname, pic, width, height, side );
+ if( !Q_stricmp( extension, ".wal" ) )
+ {
+ if( flags )
+ *flags |= IT_WAL;
+ return LoadWAL( pathname, pic, width, height, side );
+ }
+ }
+
+ return 0;
+}
+
+/*
+================
+R_FlipTexture
+================
+*/
+static void R_FlipTexture( const qbyte *in, qbyte *out, int width, int height, int samples, qboolean flipx, qboolean flipy, qboolean flipdiagonal )
+{
+ int i, x, y;
+ const qbyte *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;
+ qbyte *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 = (qbyte *)inrow + p1[j];
+ pix2 = (qbyte *)inrow + p2[j];
+ pix3 = (qbyte *)inrow2 + p1[j];
+ pix4 = (qbyte *)inrow2 + p2[j];
+ ( ( qbyte * )( out + j ) )[0] = ( pix1[0] + pix2[0] + pix3[0] + pix4[0] ) >> 2;
+ ( ( qbyte * )( out + j ) )[1] = ( pix1[1] + pix2[1] + pix3[1] + pix4[1] ) >> 2;
+ ( ( qbyte * )( out + j ) )[2] = ( pix1[2] + pix2[2] + pix3[2] + pix4[2] ) >> 2;
+ ( ( qbyte * )( out + j ) )[3] = ( pix1[3] + pix2[3] + pix3[3] + pix4[3] ) >> 2;
+ }
+ }
+}
+
+/*
+================
+R_HeightmapToNormalmap
+================
+*/
+static int R_HeightmapToNormalmap( const qbyte *in, qbyte *out, int width, int height, float bumpScale )
+{
+ int x, y;
+ vec3_t n;
+ float ibumpScale;
+ const qbyte *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, qbyte *in, int iwidth, int iheight )
+{
+ const char *p;
+ int width, height, samples;
+ qbyte *pic, *pic2;
+ char *depthName;
+ size_t depthNameSize;
+
+ ENSUREBUFSIZE( imagePathBuf2, strlen( pathname ) + (strlen( "depth" ) + 1) + 5 );
+ depthName = r_imagePathBuf2;
+ depthNameSize = r_sizeof_imagePathBuf2;
+
+ Q_strncpyz( depthName, pathname, depthNameSize );
+
+ p = Q_strrstr( pathname, "_norm" );
+ if( !p )
+ p = pathname + strlen( pathname );
+ Q_strncpyz( depthName + (p - pathname), "_depth", depthNameSize - (p - pathname) );
+
+ pic = NULL;
+ samples = R_LoadImageFromDisk( depthName, depthNameSize, &pic, &width, &height, NULL, 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
+ {
+ Com_Printf( S_COLOR_YELLOW "WARNING: different depth map dimensions differ from parent (%s)\n", depthName );
+ }
+ }
+
+ return 3;
+}
+
+/*
+================
+R_CutImage
+================
+*/
+static void R_CutImage( qbyte *in, int inwidth, int height, qbyte *out, int x, int y, int outwidth, int outheight )
+{
+ int i, j;
+ int *iin, *iout;
+
+ if( x + outwidth > inwidth )
+ outwidth = inwidth - x;
+ if( y + outheight > height )
+ outheight = height - y;
+
+ iout = (int *)out;
+ for( i = 0; i < outheight; i++ )
+ {
+ iin = (int *)in + (y + i) * inwidth + x;
+ for( j = 0; j < outwidth; j++, iin++, iout++ )
+ *iout = *iin;
+ }
+}
+
+/*
+================
+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( qbyte *in, int width, int height )
+{
+ int i, j;
+ qbyte *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, qboolean 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( qbyte **data, int width, int height, int flags, int *upload_width, int *upload_height, int *samples, qboolean subImage )
+{
+ int i, c, comp, format;
+ int target, target2;
+ int numTextures;
+ unsigned *scaled = NULL;
+ int scaledWidth, scaledHeight;
+ qboolean driverMipmap = glConfig.ext.generate_mipmap && !(flags & IT_CUBEMAP);
+
+ assert( samples );
+
+ // we can't properly mipmap a NPT-texture in software
+ if( glConfig.ext.texture_non_power_of_two && ( driverMipmap || (flags & IT_NOMIPMAP) ) )
+ {
+ 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;
+ clamp( scaledWidth, 1, glConfig.maxTextureCubemapSize );
+ clamp( scaledHeight, 1, glConfig.maxTextureCubemapSize );
+ }
+ else
+ {
+ numTextures = 1;
+ target = GL_TEXTURE_2D;
+ target2 = GL_TEXTURE_2D;
+ clamp( scaledWidth, 1, glConfig.maxTextureSize );
+ clamp( scaledHeight, 1, 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_LUMINANCE )
+ {
+ *samples = 1;
+ }
+ else if( flags & ( IT_NORGB|IT_NOALPHA ) )
+ {
+ qbyte *scan;
+
+ if( flags & IT_NORGB )
+ {
+ for( i = 0; i < numTextures && data[i]; i++ )
+ {
+ scan = ( qbyte * )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 = ( qbyte * )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 if( flags & IT_LUMINANCE )
+ {
+ comp = GL_LUMINANCE;
+ format = GL_LUMINANCE;
+ }
+ else
+ {
+ comp = R_TextureFormat( *samples, flags & IT_NOCOMPRESS );
+ format = GL_RGBA;
+ }
+
+ if( flags & IT_NOFILTERING )
+ {
+ qglTexParameteri( target, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+ qglTexParameteri( target, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+ }
+ else if( flags & IT_DEPTH )
+ {
+ qglTexParameteri( target, GL_TEXTURE_MIN_FILTER, gl_filter_depth );
+ qglTexParameteri( target, GL_TEXTURE_MAG_FILTER, gl_filter_depth );
+
+ if( glConfig.ext.texture_filter_anisotropic )
+ qglTexParameteri( target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 );
+ }
+ else if( !( flags & IT_NOMIPMAP ) )
+ {
+ qglTexParameteri( target, GL_TEXTURE_MIN_FILTER, gl_filter_min );
+ qglTexParameteri( target, GL_TEXTURE_MAG_FILTER, gl_filter_max );
+
+ if( glConfig.ext.texture_filter_anisotropic )
+ qglTexParameteri( target, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_anisotropic_filter );
+ }
+ else
+ {
+ qglTexParameteri( target, GL_TEXTURE_MIN_FILTER, gl_filter_max );
+ qglTexParameteri( target, GL_TEXTURE_MAG_FILTER, gl_filter_max );
+
+ if( glConfig.ext.texture_filter_anisotropic )
+ qglTexParameteri( target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 );
+ }
+
+ // clamp if required
+ if( !( flags & IT_CLAMP ) )
+ {
+ qglTexParameteri( target, GL_TEXTURE_WRAP_S, GL_REPEAT );
+ qglTexParameteri( target, GL_TEXTURE_WRAP_T, GL_REPEAT );
+ }
+ else if( glConfig.ext.texture_edge_clamp )
+ {
+ qglTexParameteri( target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
+ qglTexParameteri( target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
+ }
+ else
+ {
+ qglTexParameteri( target, GL_TEXTURE_WRAP_S, GL_CLAMP );
+ qglTexParameteri( target, GL_TEXTURE_WRAP_T, GL_CLAMP );
+ }
+
+ if( ( scaledWidth == width ) && ( scaledHeight == height ) && ( flags & IT_NOMIPMAP ) )
+ {
+ if( subImage )
+ {
+ for( i = 0; i < numTextures; i++, target2++ )
+ qglTexSubImage2D( target2, 0, 0, 0, scaledWidth, scaledHeight, format, GL_UNSIGNED_BYTE, data[i] );
+ }
+ else
+ {
+ for( i = 0; i < numTextures; i++, target2++ )
+ qglTexImage2D( target2, 0, comp, scaledWidth, scaledHeight, 0, format, GL_UNSIGNED_BYTE, data[i] );
+ }
+ }
+ else
+ {
+ 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 )
+ qglTexParameteri( target2, GL_GENERATE_MIPMAP_SGIS, GL_TRUE );
+
+ if( subImage )
+ qglTexSubImage2D( target2, 0, 0, 0, scaledWidth, scaledHeight, format, GL_UNSIGNED_BYTE, mip );
+ else
+ qglTexImage2D( 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( (qbyte *)mip, w, h );
+
+ w >>= 1;
+ h >>= 1;
+ if( w < 1 )
+ w = 1;
+ if( h < 1 )
+ h = 1;
+ miplevel++;
+
+ if( subImage )
+ qglTexSubImage2D( target2, miplevel, 0, 0, w, h, format, GL_UNSIGNED_BYTE, mip );
+ else
+ qglTexImage2D( 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( qbyte **data, int width, int height, int depth, int flags, int *upload_width, int *upload_height, int *upload_depth, int *samples, qboolean subImage )
+{
+ int comp, format;
+ int scaledWidth, scaledHeight, scaledDepth;
+
+ assert( samples );
+ assert( ( flags & (IT_NOMIPMAP|IT_NOPICMIP) ) );
+
+ 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 )
+ Com_Error( ERR_DROP, "R_Upload32_3D: bad texture dimensions (not a power of 2)" );
+ if( scaledWidth > glConfig.maxTextureSize3D || scaledHeight > glConfig.maxTextureSize3D || scaledDepth > glConfig.maxTextureSize3D )
+ Com_Error( ERR_DROP, "R_Upload32_3D: texture is too large (resizing is not supported)" );
+
+ if( upload_width )
+ *upload_width = scaledWidth;
+ if( upload_height )
+ *upload_height = scaledHeight;
+ if( upload_depth )
+ *upload_depth = scaledDepth;
+
+ if( flags & IT_LUMINANCE )
+ {
+ comp = GL_LUMINANCE;
+ format = GL_LUMINANCE;
+ }
+ else
+ {
+ comp = R_TextureFormat( *samples, flags & IT_NOCOMPRESS );
+ format = GL_RGBA;
+ }
+
+ if( !( flags & IT_NOMIPMAP ) )
+ {
+ qglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, gl_filter_min );
+ qglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, gl_filter_max );
+
+ if( glConfig.ext.texture_filter_anisotropic )
+ qglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_anisotropic_filter );
+ }
+ else
+ {
+ qglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, gl_filter_max );
+ qglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, gl_filter_max );
+
+ if( glConfig.ext.texture_filter_anisotropic )
+ qglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 );
+ }
+
+ // clamp if required
+ if( !( flags & IT_CLAMP ) )
+ {
+ qglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT );
+ qglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT );
+ qglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT );
+ }
+ else if( glConfig.ext.texture_edge_clamp )
+ {
+ qglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
+ qglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
+ qglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE );
+ }
+ else
+ {
+ qglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP );
+ qglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP );
+ qglTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP );
+ }
+
+ if( subImage )
+ qglTexSubImage3D( GL_TEXTURE_3D, 0, 0, 0, 0, scaledWidth, scaledHeight, scaledDepth, format, GL_UNSIGNED_BYTE, data[0] );
+ else
+ qglTexImage3D( GL_TEXTURE_3D, 0, comp, scaledWidth, scaledHeight, scaledDepth, 0, format, GL_UNSIGNED_BYTE, data[0] );
+}
+
+/*
+================
+R_InitPic
+================
+*/
+static inline image_t *R_InitPic( const char *name, qbyte **pic, int width, int height, int depth, int flags, int samples )
+{
+ image_t *image;
+
+ if( r_numImages == MAX_GLIMAGES )
+ Com_Error( ERR_DROP, "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->fbo = 0;
+
+ // 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;
+
+ qglGenTextures( 1, &image->texnum );
+ GL_Bind( 0, image );
+
+ if( depth == 1 )
+ R_Upload32( pic, width, height, flags, &image->upload_width, &image->upload_height, &image->samples, qfalse );
+ else
+ R_Upload32_3D_Fast( pic, width, height, depth, flags, &image->upload_width, &image->upload_height, &image->upload_depth, &image->samples, qfalse );
+
+ if( flags & IT_FRAMEBUFFER )
+ {
+ image->fbo = R_RegisterFBObject ();
+ if( image->fbo
+ &&! R_AttachTextureToFBOject( image->fbo, image, (image->flags & IT_DEPTH ? qtrue : qfalse) ) )
+ {
+ Com_Printf( S_COLOR_YELLOW "Warning: Error attaching texture to a FBO: %s\n", image->name );
+ image->fbo = 0;
+ }
+ }
+
+ image_cur_hash = IMAGES_HASH_SIZE+1;
+ return image;
+}
+
+/*
+================
+R_LoadPic
+================
+*/
+image_t *R_LoadPic( const char *name, qbyte **pic, int width, int height, int flags, int samples )
+{
+ qbyte **data = pic;
+
+ if( flags & IT_MIPTEX )
+ samples = LoadMipTex( data, width, height, flags );
+
+ if( !(flags & IT_CUBEMAP) )
+ {
+ qbyte *temp;
+
+ if( flags & IT_LEFTHALF )
+ {
+ temp = R_PrepareImageBuffer( TEXTURE_CUT_BUF, width/2 * height * 4 );
+ R_CutImage( *data, width, height, temp, 0, 0, width/2, height );
+ data = &temp;
+ width /= 2;
+ }
+ else if( flags & IT_RIGHTHALF )
+ {
+ temp = R_PrepareImageBuffer( TEXTURE_CUT_BUF, width/2 * height * 4 );
+ R_CutImage( *data, width, height, temp, width/2, 0, width/2, height );
+ data = &temp;
+ width /= 2;
+ }
+
+ if( flags & ( IT_FLIPX|IT_FLIPY|IT_FLIPDIAGONAL ) )
+ {
+ temp = R_PrepareImageBuffer( TEXTURE_FLIPPING_BUF0, width * height * 4 );
+ R_FlipTexture( *data, temp, width, height, 4, (flags & IT_FLIPX), (flags & IT_FLIPY), (flags & IT_FLIPDIAGONAL) );
+ data = &temp;
+ }
+ }
+
+ return R_InitPic( name, data, width, height, 1, flags, samples );
+}
+
+/*
+================
+R_Load3DPic
+================
+*/
+image_t *R_Load3DPic( const char *name, qbyte **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;
+ const char *extension = "";
+ size_t pathsize;
+
+ if( !name || !name[0] )
+ return NULL; // Com_Error (ERR_DROP, "R_FindImage: NULL name");
+
+ ENSUREBUFSIZE( imagePathBuf, strlen( name ) + (suffix ? strlen( suffix ) : 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;
+
+ if( lastDot != -1 )
+ {
+ len = lastDot;
+ extension = &name[len];
+ }
+
+ pathname[len] = 0;
+ if( suffix )
+ {
+ 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 ) == 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 ) == flags ) && !strcmp( image->name, pathname ) )
+ return image;
+ }
+ }
+
+ pathname[len] = 0;
+ image = NULL;
+
+ //
+ // load the pic from disk
+ //
+ if( flags & IT_CUBEMAP )
+ {
+ qbyte *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;
+
+ Q_strncatz( pathname, extension, pathsize );
+ samples = R_LoadImageFromDisk( pathname, pathsize, &(pic[j]), &width, &height, &flags, j );
+ if( pic[j] )
+ {
+ if( width != height )
+ {
+ Com_Printf( "Not square cubemap image %s\n", pathname );
+ break;
+ }
+ if( !j )
+ {
+ lastSize = width;
+ }
+ else if( lastSize != width )
+ {
+ Com_Printf( "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;
+ qbyte *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] = '.';
+ Q_strncpyz( &image->extension[1], &pathname[len+4], sizeof( image->extension )-1 );
+ }
+ }
+ else
+ {
+ qbyte *pic;
+
+ Q_strncatz( pathname, extension, pathsize );
+ samples = R_LoadImageFromDisk( pathname, pathsize, &pic, &width, &height, &flags, 0 );
+
+ if( pic )
+ {
+ qbyte *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;
+ }
+
+ pathname[len] = 0;
+ image = R_LoadPic( pathname, &pic, width, height, flags, samples );
+ image->extension[0] = '.';
+ Q_strncpyz( &image->extension[1], &pathname[len+1], sizeof( image->extension )-1 );
+ }
+ }
+
+ return image;
+}
+
+/*
+==============================================================================
+
+SCREEN SHOTS
+
+==============================================================================
+*/
+
+/*
+==================
+R_ScreenShot
+==================
+*/
+static void R_ScreenShot( const char *name, qboolean silent )
+{
+ char *checkname = NULL;
+ size_t checkname_size;
+ qbyte *buffer;
+
+ if( name && name[0] && Q_stricmp(name, "*") )
+ {
+ checkname_size = sizeof( char ) * ( strlen( "screenshots/" ) + strlen( name ) + strlen( ".jpg" ) + 1 );
+ checkname = Mem_TempMalloc( checkname_size );
+ Q_snprintfz( checkname, checkname_size, "screenshots/%s", name );
+
+ COM_SanitizeFilePath( checkname );
+
+ if( !COM_ValidateRelativeFilename( checkname ) )
+ {
+ Com_Printf( "Invalid filename\n" );
+ Mem_Free( checkname );
+ return;
+ }
+
+ if( r_screenshot_jpeg->integer )
+ COM_DefaultExtension( checkname, ".jpg", checkname_size );
+ else
+ COM_DefaultExtension( checkname, ".tga", checkname_size );
+ }
+
+ //
+ // find a file name to save it to
+ //
+ if( !checkname )
+ {
+ static int lastIndex = 0;
+
+ checkname_size = sizeof( char ) * ( strlen( "screenshots/wsw" ) + 5 + strlen( ".jpg" ) + 1 );
+ checkname = Mem_TempMalloc( checkname_size );
+
+ // force a rescan
+ if( r_screenshot_jpeg->modified )
+ {
+ lastIndex = 0;
+ r_screenshot_jpeg->modified = qfalse;
+ }
+
+ for( ; lastIndex < 100000; lastIndex++ )
+ {
+ if( r_screenshot_jpeg->integer )
+ Q_snprintfz( checkname, checkname_size, "screenshots/wsw%05i.jpg", lastIndex );
+ else
+ Q_snprintfz( checkname, checkname_size, "screenshots/wsw%05i.tga", lastIndex );
+ if( FS_FOpenFile( checkname, NULL, FS_READ ) == -1 )
+ break; // file doesn't exist
+ }
+
+ if( lastIndex == 100000 )
+ {
+ Com_Printf( "Couldn't create a file\n" );
+ Mem_Free( checkname );
+ return;
+ }
+
+ lastIndex++;
+ }
+
+ if( r_screenshot_jpeg->integer )
+ {
+ buffer = Mem_Alloc( r_texturesPool, glState.width * glState.height * 3 );
+ qglReadPixels( 0, 0, glState.width, glState.height, GL_RGB, GL_UNSIGNED_BYTE, buffer );
+
+ if( WriteJPG( checkname, buffer, glState.width, glState.height, r_screenshot_jpeg_quality->integer ) && !silent )
+ Com_Printf( "Wrote %s\n", checkname );
+ }
+ else
+ {
+ buffer = Mem_Alloc( r_texturesPool, 18 + glState.width * glState.height * 3 );
+ qglReadPixels( 0, 0, glState.width, glState.height, glConfig.ext.bgra ? GL_BGR_EXT : GL_RGB, GL_UNSIGNED_BYTE, buffer + 18 );
+
+ if( WriteTGA( checkname, buffer, glState.width, glState.height, glConfig.ext.bgra ) && !silent )
+ Com_Printf( "Wrote %s\n", checkname );
+ }
+
+ Mem_Free( buffer );
+ Mem_Free( checkname );
+}
+
+/*
+==================
+R_ScreenShot_f
+==================
+*/
+void R_ScreenShot_f( void )
+{
+ R_ScreenShot( Cmd_Argv( 1 ), Cmd_Argc() >= 3 && !Q_stricmp( Cmd_Argv( 2 ), "silent" ) );
+}
+
+/*
+==================
+R_EnvShot_f
+==================
+*/
+void R_EnvShot_f( void )
+{
+ int i;
+ int size, maxSize;
+ qbyte *buffer, *bufferFlipped;
+ int checkname_size;
+ char *checkname;
+ struct cubemapSufAndFlip
+ {
+ char *suf; vec3_t angles; int flags;
+ } cubemapShots[6] = {
+ { "px", { 0, 0, 0 }, IT_FLIPX|IT_FLIPY|IT_FLIPDIAGONAL },
+ { "nx", { 0, 180, 0 }, IT_FLIPDIAGONAL },
+ { "py", { 0, 90, 0 }, IT_FLIPY },
+ { "ny", { 0, 270, 0 }, IT_FLIPX },
+ { "pz", { -90, 180, 0 }, IT_FLIPDIAGONAL },
+ { "nz", { 90, 180, 0 }, IT_FLIPDIAGONAL }
+ };
+
+ if( !r_worldmodel )
+ return;
+
+ if( Cmd_Argc() != 3 )
+ {
+ Com_Printf( "usage: envshot <name> <size>\n" );
+ return;
+ }
+
+ maxSize = min( glState.width, glState.height );
+ if( maxSize > atoi( Cmd_Argv( 2 ) ) )
+ maxSize = atoi( Cmd_Argv( 2 ) );
+
+ for( size = 1; size < maxSize; size <<= 1 ) ;
+ if( size > maxSize )
+ size >>= 1;
+
+ // do not render non-bmodel entities
+ ri.params |= RP_ENVVIEW;
+
+ buffer = Mem_Alloc( r_texturesPool, (size * size * 3) * 2 + 18 );
+ bufferFlipped = buffer + size * size * 3;
+
+ checkname_size = sizeof( char ) * ( strlen( "env/" ) + strlen( Cmd_Argv( 1 ) ) + 1 + strlen( cubemapShots[0].suf ) + 4 + 1 );
+ checkname = Mem_TempMalloc( checkname_size );
+
+ for( i = 0; i < 6; i++ )
+ {
+ R_DrawCubemapView( r_lastRefdef.vieworg, cubemapShots[i].angles, size );
+
+ qglReadPixels( 0, glState.height - size, size, size, glConfig.ext.bgra ? GL_BGR_EXT : GL_RGB, GL_UNSIGNED_BYTE, buffer );
+
+ R_FlipTexture( buffer, bufferFlipped + 18, size, size, 3, ( cubemapShots[i].flags & IT_FLIPX ), ( cubemapShots[i].flags & IT_FLIPY ), ( cubemapShots[i].flags & IT_FLIPDIAGONAL ) );
+
+ Q_snprintfz( checkname, checkname_size, "env/%s_%s", Cmd_Argv( 1 ), cubemapShots[i].suf );
+ //if( r_screenshot_jpeg->integer ) {
+ // COM_DefaultExtension( checkname, ".jpg", sizeof(checkname) );
+ // if( WriteJPG( checkname, bufferFlipped, size, size, r_screenshot_jpeg_quality->integer ) )
+ // Com_Printf( "Wrote envshot %s\n", checkname );
+ //} else {
+ COM_DefaultExtension( checkname, ".tga", checkname_size );
+ if( WriteTGA( checkname, bufferFlipped, size, size, glConfig.ext.bgra ) )
+ Com_Printf( "Wrote envshot %s\n", checkname );
+ //}
+ }
+
+ // render non-bmodel entities again
+ ri.params &= ~RP_ENVVIEW;
+
+ Mem_Free( checkname );
+ Mem_Free( buffer );
+}
+
+/*
+==================
+R_BeginAviDemo
+==================
+*/
+void R_BeginAviDemo( void )
+{
+ if( r_aviBuffer )
+ Mem_Free( r_aviBuffer );
+ r_aviBuffer = Mem_Alloc( r_texturesPool, 18 + glState.width * glState.height * 3 );
+}
+
+/*
+==================
+R_WriteAviFrame
+==================
+*/
+void R_WriteAviFrame( int frame, qboolean scissor )
+{
+ int x, y, w, h;
+ int checkname_size;
+ char *checkname;
+
+ if( !r_aviBuffer )
+ return;
+
+ if( scissor )
+ {
+ x = r_lastRefdef.x;
+ y = glState.height - r_lastRefdef.height - r_lastRefdef.y;
+ w = r_lastRefdef.width;
+ h = r_lastRefdef.height;
+ }
+ else
+ {
+ x = 0;
+ y = 0;
+ w = glState.width;
+ h = glState.height;
+ }
+
+ checkname_size = sizeof( char ) * ( strlen( "avi/avi" ) + 6 + 4 + 1 );
+ checkname = Mem_TempMalloc( checkname_size );
+ Q_snprintfz( checkname, checkname_size, "avi/avi%06i", frame );
+
+ if( r_screenshot_jpeg->integer )
+ {
+ COM_DefaultExtension( checkname, ".jpg", checkname_size );
+ qglReadPixels( x, y, w, h, GL_RGB, GL_UNSIGNED_BYTE, r_aviBuffer );
+ WriteJPG( checkname, r_aviBuffer, w, h, r_screenshot_jpeg_quality->integer );
+ }
+ else
+ {
+ COM_DefaultExtension( checkname, ".tga", checkname_size );
+ qglReadPixels( x, y, w, h, glConfig.ext.bgra ? GL_BGR_EXT : GL_RGB, GL_UNSIGNED_BYTE, r_aviBuffer + 18 );
+ WriteTGA( checkname, r_aviBuffer, w, h, glConfig.ext.bgra );
+ }
+
+ Mem_Free( checkname );
+}
+
+/*
+==================
+R_StopAviDemo
+==================
+*/
+void R_StopAviDemo( void )
+{
+ if( r_aviBuffer )
+ {
+ Mem_Free( r_aviBuffer );
+ r_aviBuffer = NULL;
+ }
+}
+
+//=======================================================
+
+/*
+==================
+R_InitNoTexture
+==================
+*/
+static qbyte *R_InitNoTexture( int *w, int *h, int *depth, int *flags, int *samples )
+{
+ int x, y;
+ qbyte *data;
+ qbyte 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 qbyte *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;
+ qbyte *data;
+
+ //
+ // dynamic light texture
+ //
+ if( glConfig.ext.texture3D )
+ {
+ size = 32;
+ *depth = 32;
+ }
+ else
+ {
+ size = 64;
+ *depth = 1;
+ }
+
+ *w = *h = size;
+ *flags = IT_NOPICMIP|IT_NOMIPMAP|IT_CLAMP|IT_NOCOMPRESS|IT_LUMINANCE;
+ *samples = 1;
+
+ data = R_PrepareImageBuffer( TEXTURE_LOADING_BUF0, size * size * *depth );
+ 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)*depth ) - 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] = d;
+ }
+ }
+ }
+ return data;
+}
+
+/*
+==================
+R_InitSolidColorTexture
+==================
+*/
+static qbyte *R_InitSolidColorTexture( int *w, int *h, int *depth, int *flags, int *samples, int color )
+{
+ qbyte *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 qbyte *R_InitParticleTexture( int *w, int *h, int *depth, int *flags, int *samples )
+{
+ int x, y;
+ int dx2, dy, d;
+ qbyte *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 qbyte *R_InitWhiteTexture( int *w, int *h, int *depth, int *flags, int *samples )
+{
+ return R_InitSolidColorTexture( w, h, depth, flags, samples, 255 );
+}
+
+/*
+==================
+R_InitBlackTexture
+==================
+*/
+static qbyte *R_InitBlackTexture( int *w, int *h, int *depth, int *flags, int *samples )
+{
+ return R_InitSolidColorTexture( w, h, depth, flags, samples, 0 );
+}
+
+/*
+==================
+R_InitBlankBumpTexture
+==================
+*/
+static qbyte *R_InitBlankBumpTexture( int *w, int *h, int *depth, int *flags, int *samples )
+{
+ qbyte *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 qbyte *R_InitFogTexture( int *w, int *h, int *depth, int *flags, int *samples )
+{
+ qbyte *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] = (qbyte)( min( t, 255.0 ) );
+ }
+ data[y*4+3] = 0;
+ }
+ return data;
+}
+
+/*
+==================
+R_InitCoronaTexture
+==================
+*/
+static qbyte *R_InitCoronaTexture( int *w, int *h, int *depth, int *flags, int *samples )
+{
+ int x, y, a;
+ float dx, dy;
+ qbyte *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 ) ) );
+ clamp( a, 0, 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, const char *key, int id, int screenWidth, int screenHeight, int size, int flags, int samples, qboolean screenLimit )
+{
+ int limit;
+ int width, height;
+ image_t *t;
+
+ // limit the texture size to either screen resolution in case we can't use FBO
+ // or hardware limits and ensure it's a POW2-texture if we don't support such textures
+ limit = glConfig.maxTextureSize;
+ if( size )
+ limit = min( limit, size );
+ if( limit < 1 )
+ limit = 1;
+ width = height = limit;
+
+ if( glConfig.ext.texture_non_power_of_two )
+ {
+ if( screenLimit )
+ {
+ width = min( screenWidth, limit );
+ height = min( screenHeight, limit );
+ }
+ }
+ else
+ {
+ if( screenLimit )
+ limit = min( limit, min( screenWidth, screenHeight ) );
+ for( size = 2; size <= limit; size <<= 1 );
+ width = height = size >> 1;
+ }
+
+ // create a new texture or update the old one
+ if( !( *texture ) || ( *texture )->width != width || ( *texture )->height != height )
+ {
+ qbyte *data = NULL;
+
+ if( !*texture )
+ {
+ char uploadName[128];
+
+ if( key && *key )
+ Q_snprintfz( uploadName, sizeof( uploadName ), "***%s_%s_%i***", name, key, id );
+ else
+ Q_snprintfz( uploadName, sizeof( uploadName ), "***%s_%i***", name, id );
+ *texture = R_LoadPic( uploadName, &data, width, height, flags, samples );
+ return;
+ }
+
+ t = *texture;
+ GL_Bind( 0, t );
+ t->width = width;
+ t->height = height;
+ R_Upload32( &data, width, height, flags, &t->upload_width, &t->upload_height, &t->samples, qfalse );
+
+ // update FBO if attached
+ if( t->fbo )
+ {
+ if( !R_AttachTextureToFBOject( t->fbo, t, (flags & IT_DEPTH ? qtrue : qfalse) ) )
+ {
+ Com_Printf( S_COLOR_YELLOW "Warning: Error attaching texture to a FBO: %s\n", t->name );
+ t->fbo = 0;
+ }
+ }
+ }
+}
+
+/*
+==================
+R_InitPortalTexture
+==================
+*/
+void R_InitPortalTexture( image_t **texture, const char *key, int id, int screenWidth, int screenHeight, int flags )
+{
+ int size = r_portalmaps_maxtexsize->integer ? r_portalmaps_maxtexsize->integer : glConfig.maxTextureSize;
+ qboolean screenLimit = qtrue;
+
+// if( size > glConfig.maxTextureSize / 2 ) size = glConfig.maxTextureSize / 2;
+ if( size > 2048 ) size = 2048;
+
+ R_InitScreenTexture( texture, "r_portaltexture", key, id, screenWidth, screenHeight, size, IT_PORTALMAP|IT_FRAMEBUFFER|flags, 3, screenLimit );
+}
+
+/*
+==================
+R_InitShadowmapTexture
+==================
+*/
+void R_InitShadowmapTexture( image_t **texture, int id, int screenWidth, int screenHeight, int flags )
+{
+ int size = r_shadows_maxtexsize->integer;
+ qboolean screenLimit = (!glConfig.ext.framebuffer_object ? qtrue : qfalse);
+
+ R_InitScreenTexture( texture, "r_shadowmap", NULL, id, screenWidth, screenHeight, size, IT_SHADOWMAP|IT_FRAMEBUFFER|flags, 1, screenLimit );
+}
+
+
+/*
+===============
+R_FindPortalTextureSlot
+===============
+*/
+int R_FindPortalTextureSlot( const char *key, int id )
+{
+ int i;
+ image_t *image;
+ char uploadName[128];
+ const char *name = "r_portaltexture";
+
+ if( key && *key )
+ Q_snprintfz( uploadName, sizeof( uploadName ), "***%s_%s_%i***", name, key, id );
+ else
+ Q_snprintfz( uploadName, sizeof( uploadName ), "***%s_%i***", name, id );
+
+ for( i = 0; ; i++ )
+ {
+ image = r_portaltextures[i];
+ if( !image )
+ break;
+
+ if( !strcmp( image->name, uploadName ) )
+ return i+1;
+ }
+
+ if( i == MAX_PORTAL_TEXTURES )
+ return 0;
+
+ return i+1;
+}
+
+/*
+==================
+R_InitCinematicTexture
+==================
+*/
+static void R_InitCinematicTexture( void )
+{
+ // reserve a dummy texture slot
+ r_cintexture = &images[r_numImages++];
+ qglGenTextures( 1, &r_cintexture->texnum );
+ r_cintexture->depth = 1;
+}
+
+/*
+==================
+R_InitBuiltinTextures
+==================
+*/
+static void R_InitBuiltinTextures( void )
+{
+ qbyte *data;
+ int w, h, depth, flags, samples;
+ image_t *image;
+ const struct
+ {
+ char *name;
+ image_t **image;
+ qbyte *( *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 );
+ assert( data );
+
+ 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_InitImages
+===============
+*/
+void R_InitImages( void )
+{
+ r_texturesPool = Mem_AllocPool( NULL, "Textures" );
+ image_cur_hash = IMAGES_HASH_SIZE+1;
+
+ r_imagePathBuf = r_imagePathBuf2 = NULL;
+ r_sizeof_imagePathBuf = r_sizeof_imagePathBuf2 = 0;
+
+ r_8to24table = NULL;
+
+ R_InitCinematicTexture();
+ R_InitBuiltinTextures();
+ R_InitBloomTextures();
+}
+
+/*
+===============
+R_ShutdownImages
+===============
+*/
+void R_ShutdownImages( void )
+{
+ int i;
+
+ if( !r_texturesPool )
+ return;
+
+ R_StopAviDemo ();
+
+ R_FreeImageBuffers ();
+
+ for( i = 0; i < r_numImages; i++ )
+ {
+ qglDeleteTextures( 1, &images[i].texnum );
+ Mem_Free( images[i].name );
+ }
+
+ if( r_imagePathBuf )
+ Mem_Free( r_imagePathBuf );
+ if( r_imagePathBuf2 )
+ Mem_Free( r_imagePathBuf2 );
+
+ if( r_8to24table )
+ {
+ Mem_Free( r_8to24table );
+ r_8to24table = NULL;
+ }
+
+ Mem_FreePool( &r_texturesPool );
+
+ memset( r_portaltextures, 0, sizeof( image_t * ) * MAX_PORTAL_TEXTURES );
+ 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 ) );
+}