From f23d273a395cbdfd8a1197adc3509ba3dfb4637b Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Mon, 1 Mar 2010 20:40:50 +0100 Subject: update Signed-off-by: Florian Pritz --- warsow/libjpeg.patch | 20 + warsow/r_image.c.new | 2967 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2987 insertions(+) create mode 100644 warsow/libjpeg.patch create mode 100644 warsow/r_image.c.new (limited to 'warsow') diff --git a/warsow/libjpeg.patch b/warsow/libjpeg.patch new file mode 100644 index 0000000..54803a0 --- /dev/null +++ b/warsow/libjpeg.patch @@ -0,0 +1,20 @@ +--- warsow-src/source/ref_gl/r_image.c 2009-06-03 21:52:13.000000000 +0200 ++++ warsow-src/source/ref_gl/r_image.c.new 2010-01-31 21:04:41.000000000 +0100 +@@ -924,7 +924,7 @@ + cinfo->src->bytes_in_buffer -= (size_t) num_bytes; + } + +-static void jpeg_mem_src( j_decompress_ptr cinfo, qbyte *mem, int len ) ++/*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, +@@ -938,7 +938,7 @@ + cinfo->src->bytes_in_buffer = len; + cinfo->src->next_input_byte = mem; + } +- ++*/ + /* + ============= + LoadJPG 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 \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 ) ); +} -- cgit v1.2.3-24-g4f1b