/* 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 "gl_local.h" #include "gl_refl.h" /** MPO **/ #if defined(__unix__) #include #include #endif image_t gltextures[MAX_GLTEXTURES]; int numgltextures; int base_textureid; // gltextures[i] = base_textureid+i static byte intensitytable[256]; static unsigned char gammatable[256]; cvar_t *intensity; unsigned d_8to24table[256]; qboolean GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean is_sky ); qboolean GL_Upload32 (unsigned *data, int width, int height, qboolean mipmap); int gl_solid_format = 3; int gl_alpha_format = 4; int gl_tex_solid_format = 3; int gl_tex_alpha_format = 4; int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST; int gl_filter_max = GL_LINEAR; void GL_SetTexturePalette( unsigned palette[256] ) { int i; unsigned char temptable[768]; if ( qglColorTableEXT && gl_ext_palettedtexture->value ) { for ( i = 0; i < 256; i++ ) { temptable[i*3+0] = ( palette[i] >> 0 ) & 0xff; temptable[i*3+1] = ( palette[i] >> 8 ) & 0xff; temptable[i*3+2] = ( palette[i] >> 16 ) & 0xff; } qglColorTableEXT( GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, temptable ); } } void GL_EnableMultitexture( qboolean enable ) { if ( !qglSelectTextureSGIS && !qglActiveTextureARB ) return; if ( enable ) { GL_SelectTexture( GL_TEXTURE1 ); qglEnable( GL_TEXTURE_2D ); GL_TexEnv( GL_REPLACE ); } else { GL_SelectTexture( GL_TEXTURE1 ); qglDisable( GL_TEXTURE_2D ); GL_TexEnv( GL_REPLACE ); } GL_SelectTexture( GL_TEXTURE0 ); GL_TexEnv( GL_REPLACE ); } void GL_Enable3dTextureUnit( qboolean enable ) { if ( !qglSelectTextureSGIS && !qglActiveTextureARB ) return; if ( enable ) { GL_SelectTexture( GL_TEXTURE2 ); qglEnable( GL_TEXTURE_2D ); GL_TexEnv( GL_REPLACE ); } else { GL_SelectTexture( GL_TEXTURE2 ); qglDisable( GL_TEXTURE_2D ); GL_TexEnv( GL_REPLACE ); } GL_SelectTexture( GL_TEXTURE0 ); } void GL_SelectTexture( GLenum texture ) { int tmu; if ( !qglSelectTextureSGIS && !qglActiveTextureARB ) return; if ( texture == GL_TEXTURE0 ) { tmu = 0; } else if ( texture == GL_TEXTURE1 ) { tmu = 1; } else tmu = 2; if ( tmu == gl_state.currenttmu ) { return; } gl_state.currenttmu = tmu; if ( qglSelectTextureSGIS ) { qglSelectTextureSGIS( texture ); } else if ( qglActiveTextureARB ) { qglActiveTextureARB( texture ); qglClientActiveTextureARB( texture ); } } void GL_TexEnv( GLenum mode ) { static int lastmodes[2] = { -1, -1 }; if ( mode != lastmodes[gl_state.currenttmu] ) { qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode ); lastmodes[gl_state.currenttmu] = mode; } } void GL_Bind (int texnum) { extern image_t *draw_chars; if (gl_nobind->value && draw_chars) // performance evaluation option texnum = draw_chars->texnum; if ( gl_state.currenttextures[gl_state.currenttmu] == texnum) return; gl_state.currenttextures[gl_state.currenttmu] = texnum; qglBindTexture (GL_TEXTURE_2D, texnum); } void GL_Bind_2 (int texnum, image_t *tex) { GL_SelectTexture( texnum ); tex = r_notexture; qglBindTexture( GL_TEXTURE_2D, tex->texnum ); } void GL_MBind( GLenum target, int texnum ) { GL_SelectTexture( target ); if ( target == GL_TEXTURE0 ) { if ( gl_state.currenttextures[0] == texnum ) return; } else if ( target == GL_TEXTURE1 ) { if ( gl_state.currenttextures[1] == texnum ) return; } else { if ( gl_state.currenttextures[2] == texnum ) return; } GL_Bind( texnum ); } 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)) typedef struct { char *name; int mode; } gltmode_t; gltmode_t gl_alpha_modes[] = { {"default", 4}, {"GL_RGBA", GL_RGBA}, {"GL_RGBA8", GL_RGBA8}, {"GL_RGB5_A1", GL_RGB5_A1}, {"GL_RGBA4", GL_RGBA4}, {"GL_RGBA2", GL_RGBA2}, }; #define NUM_GL_ALPHA_MODES (sizeof(gl_alpha_modes) / sizeof (gltmode_t)) gltmode_t gl_solid_modes[] = { {"default", 3}, {"GL_RGB", GL_RGB}, {"GL_RGB8", GL_RGB8}, {"GL_RGB5", GL_RGB5}, {"GL_RGB4", GL_RGB4}, {"GL_R3_G3_B2", GL_R3_G3_B2}, #ifdef GL_RGB2_EXT {"GL_RGB2", GL_RGB2_EXT}, #endif }; #define NUM_GL_SOLID_MODES (sizeof(gl_solid_modes) / sizeof (gltmode_t)) /* =============== GL_TextureMode =============== */ void GL_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) { ri.Con_Printf (PRINT_ALL, "bad filter name\n"); return; } gl_filter_min = modes[i].minimize; gl_filter_max = modes[i].maximize; gl_texturemode->modified = false; ri.Con_Printf (PRINT_ALL, "Texture mode: %s\n", modes[i].name); // change all the existing mipmap texture objects for (i=0, glt=gltextures ; itype != it_pic && glt->type != it_sky ) { GL_Bind (glt->texnum); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } } } /* =============== GL_TextureAlphaMode =============== */ void GL_TextureAlphaMode( char *string ) { int i; for (i=0 ; i< NUM_GL_ALPHA_MODES ; i++) { if ( !Q_stricmp( gl_alpha_modes[i].name, string ) ) break; } if (i == NUM_GL_ALPHA_MODES) { ri.Con_Printf (PRINT_ALL, "bad alpha texture mode name\n"); return; } gl_tex_alpha_format = gl_alpha_modes[i].mode; } /* =============== GL_TextureSolidMode =============== */ void GL_TextureSolidMode( char *string ) { int i; for (i=0 ; i< NUM_GL_SOLID_MODES ; i++) { if ( !Q_stricmp( gl_solid_modes[i].name, string ) ) break; } if (i == NUM_GL_SOLID_MODES) { ri.Con_Printf (PRINT_ALL, "bad solid texture mode name\n"); return; } gl_tex_solid_format = gl_solid_modes[i].mode; } /* =============== GL_ImageList_f =============== */ void GL_ImageList_f (void) { int i; image_t *image; int texels; const char *palstrings[2] = { "RGB", "PAL" }; ri.Con_Printf (PRINT_ALL, "------------------\n"); texels = 0; for (i=0, image=gltextures ; itexnum <= 0) continue; texels += image->upload_width*image->upload_height; switch (image->type) { case it_skin: ri.Con_Printf (PRINT_ALL, "M"); break; case it_sprite: ri.Con_Printf (PRINT_ALL, "S"); break; case it_wall: ri.Con_Printf (PRINT_ALL, "W"); break; case it_pic: ri.Con_Printf (PRINT_ALL, "P"); break; default: ri.Con_Printf (PRINT_ALL, " "); break; } ri.Con_Printf (PRINT_ALL, " %3i %3i %s: %s\n", image->upload_width, image->upload_height, palstrings[image->paletted], image->name); } ri.Con_Printf (PRINT_ALL, "Total texel count (not counting mipmaps): %i\n", texels); } /* ============================================================================= scrap allocation Allocate all the little status bar obejcts into a single texture to crutch up inefficient hardware / drivers ============================================================================= */ #define MAX_SCRAPS 1 #define BLOCK_WIDTH 256 #define BLOCK_HEIGHT 256 int scrap_allocated[MAX_SCRAPS][BLOCK_WIDTH]; byte scrap_texels[MAX_SCRAPS][BLOCK_WIDTH*BLOCK_HEIGHT]; qboolean scrap_dirty; // returns a texture number and the position inside it int Scrap_AllocBlock (int w, int h, int *x, int *y) { int i, j; int best, best2; int texnum; for (texnum=0 ; texnum= best) break; if (scrap_allocated[texnum][i+j] > best2) best2 = scrap_allocated[texnum][i+j]; } if (j == w) { // this is a valid spot *x = i; *y = best = best2; } } if (best + h > BLOCK_HEIGHT) continue; for (i=0 ; ixmin = 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 || pcx->xmax >= 640 || pcx->ymax >= 480) { ri.Con_Printf (PRINT_ALL, "Bad pcx file %s\n", filename); ri.FS_FreeFile ((void *)pcx); return; } out = Q_malloc ( (pcx->ymax+1) * (pcx->xmax+1) ); *pic = out; pix = out; if (palette) { *palette = Q_malloc(768); memcpy (*palette, (byte *)pcx + len - 768, 768); } if (width) *width = pcx->xmax+1; if (height) *height = pcx->ymax+1; for (y=0 ; y<=pcx->ymax ; y++, pix += pcx->xmax+1) { for (x=0 ; x<=pcx->xmax ; ) { dataByte = *raw++; if((dataByte & 0xC0) == 0xC0) { runLength = dataByte & 0x3F; dataByte = *raw++; } else runLength = 1; while(runLength-- > 0) pix[x++] = dataByte; } } if ( raw - (byte *)pcx > len) { ri.Con_Printf (PRINT_DEVELOPER, "PCX file %s was malformed", filename); Q_free (*pic); *pic = NULL; } ri.FS_FreeFile ((void *)pcx); } /* ========================================================= TARGA LOADING ========================================================= */ // Definitions for image types #define TGA_Map 1 // Uncompressed, color-mapped images #define TGA_RGB 2 // Uncompressed, RGB images #define TGA_Mono 3 // Uncompressed, black and white images #define TGA_RLEMap 9 // Runlength encoded color-mapped images #define TGA_RLERGB 10 // Runlength encoded RGB images #define TGA_RLEMono 11 // Compressed, black and white images #define TGA_CompMap 32 // Compressed color-mapped data, using Huffman, Delta, and runlength encoding #define TGA_CompMap4 33 // Compressed color-mapped data, using Huffman, Delta, and runlength encoding. 4-pass quadtree-type process // Definitions for interleave flag #define TGA_IL_Two 1 // two-way (even/odd) interleaving #define TGA_IL_Four 2 // four way interleaving // Definitions for origin flag #define TGA_O_UPPER 0 // Origin in lower left-hand corner #define MAXCOLORS 16384 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 ============= */ void LoadTGA( char *filename, byte **pic, int *width, int *height ) { int w, h, x, y, i, temp1, temp2; int realrow, truerow, baserow, size, interleave, origin; int pixel_size, map_idx, mapped, rlencoded, RLE_count, RLE_flag; TargaHeader header; byte tmp[2], r, g, b, a, j, k, l; byte *dst, *ColorMap, *data, *pdata; // load file ri.FS_LoadFile( filename, (void **)&data ); if( !data ) return; pdata = data; header.id_length = *pdata++; header.colormap_type = *pdata++; header.image_type = *pdata++; tmp[0] = pdata[0]; tmp[1] = pdata[1]; header.colormap_index = LittleShort( *((short *)tmp) ); pdata+=2; tmp[0] = pdata[0]; tmp[1] = pdata[1]; header.colormap_length = LittleShort( *((short *)tmp) ); pdata+=2; header.colormap_size = *pdata++; header.x_origin = LittleShort( *((short *)pdata) ); pdata+=2; header.y_origin = LittleShort( *((short *)pdata) ); pdata+=2; header.width = LittleShort( *((short *)pdata) ); pdata+=2; header.height = LittleShort( *((short *)pdata) ); pdata+=2; header.pixel_size = *pdata++; header.attributes = *pdata++; if( header.id_length ) pdata += header.id_length; // validate TGA type switch( header.image_type ) { case TGA_Map: case TGA_RGB: case TGA_Mono: case TGA_RLEMap: case TGA_RLERGB: case TGA_RLEMono: break; default: ri.Sys_Error ( ERR_DROP, "LoadTGA: Only type 1 (map), 2 (RGB), 3 (mono), 9 (RLEmap), 10 (RLERGB), 11 (RLEmono) TGA images supported\n" ); return; } // validate color depth switch( header.pixel_size ) { case 8: case 15: case 16: case 24: case 32: break; default: ri.Sys_Error ( ERR_DROP, "LoadTGA: Only 8, 15, 16, 24 and 32 bit images (with colormaps) supported\n" ); return; } r = g = b = a = l = 0; // if required, read the color map information ColorMap = NULL; mapped = ( header.image_type == TGA_Map || header.image_type == TGA_RLEMap || header.image_type == TGA_CompMap || header.image_type == TGA_CompMap4 ) && header.colormap_type == 1; if( mapped ) { // validate colormap size switch( header.colormap_size ) { case 8: case 16: case 32: case 24: break; default: ri.Sys_Error ( ERR_DROP, "LoadTGA: Only 8, 16, 24 and 32 bit colormaps supported\n" ); return; } temp1 = header.colormap_index; temp2 = header.colormap_length; if( (temp1 + temp2 + 1) >= MAXCOLORS ) { ri.FS_FreeFile( data ); return; } ColorMap = (byte *)Q_malloc( MAXCOLORS * 4 ); map_idx = 0; for( i = temp1; i < temp1 + temp2; ++i, map_idx += 4 ) { // read appropriate number of bytes, break into rgb & put in map switch( header.colormap_size ) { case 8: r = g = b = *pdata++; a = 255; break; case 15: j = *pdata++; k = *pdata++; l = ((unsigned int) k << 8) + j; r = (byte) ( ((k & 0x7C) >> 2) << 3 ); g = (byte) ( (((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3 ); b = (byte) ( (j & 0x1F) << 3 ); a = 255; break; case 16: j = *pdata++; k = *pdata++; l = ((unsigned int) k << 8) + j; r = (byte) ( ((k & 0x7C) >> 2) << 3 ); g = (byte) ( (((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3 ); b = (byte) ( (j & 0x1F) << 3 ); a = (k & 0x80) ? 255 : 0; break; case 24: b = *pdata++; g = *pdata++; r = *pdata++; a = 255; l = 0; break; case 32: b = *pdata++; g = *pdata++; r = *pdata++; a = *pdata++; l = 0; break; } ColorMap[map_idx + 0] = r; ColorMap[map_idx + 1] = g; ColorMap[map_idx + 2] = b; ColorMap[map_idx + 3] = a; } } // check run-length encoding rlencoded = header.image_type == TGA_RLEMap || header.image_type == TGA_RLERGB || header.image_type == TGA_RLEMono; RLE_count = 0; RLE_flag = 0; w = header.width; h = header.height; if( width ) *width = w; if( height ) *height = h; size = w * h * 4; *pic = (byte *)Q_malloc( size ); memset( *pic, 0, size ); // read the Targa file body and convert to portable format pixel_size = header.pixel_size; origin = (header.attributes & 0x20) >> 5; interleave = (header.attributes & 0xC0) >> 6; truerow = 0; baserow = 0; for( y = 0; y < h; y++ ) { realrow = truerow; if( origin == TGA_O_UPPER ) realrow = h - realrow - 1; dst = *pic + realrow * w * 4; for( x = 0; x < w; x++ ) { // check if run length encoded if( rlencoded ) { if( !RLE_count ) { // have to restart run i = *pdata++; RLE_flag = (i & 0x80); if( !RLE_flag ) { // stream of unencoded pixels RLE_count = i + 1; } else { // single pixel replicated RLE_count = i - 127; } // decrement count & get pixel --RLE_count; } else { // have already read count & (at least) first pixel --RLE_count; if( RLE_flag ) // replicated pixels goto PixEncode; } } // read appropriate number of bytes, break into RGB switch( pixel_size ) { case 8: r = g = b = l = *pdata++; a = 255; break; case 15: j = *pdata++; k = *pdata++; l = ((unsigned int) k << 8) + j; r = (byte) ( ((k & 0x7C) >> 2) << 3 ); g = (byte) ( (((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3 ); b = (byte) ( (j & 0x1F) << 3 ); a = 255; break; case 16: j = *pdata++; k = *pdata++; l = ((unsigned int) k << 8) + j; r = (byte) ( ((k & 0x7C) >> 2) << 3 ); g = (byte) ( (((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3 ); b = (byte) ( (j & 0x1F) << 3 ); a = 255; break; case 24: b = *pdata++; g = *pdata++; r = *pdata++; a = 255; l = 0; break; case 32: b = *pdata++; g = *pdata++; r = *pdata++; a = *pdata++; l = 0; break; default: ri.Sys_Error( ERR_DROP, "Illegal pixel_size '%d' in file '%s'\n", filename ); return; } PixEncode: if ( mapped ) { map_idx = l * 4; *dst++ = ColorMap[map_idx + 0]; *dst++ = ColorMap[map_idx + 1]; *dst++ = ColorMap[map_idx + 2]; *dst++ = ColorMap[map_idx + 3]; } else { *dst++ = r; *dst++ = g; *dst++ = b; *dst++ = a; } } if (interleave == TGA_IL_Four) truerow += 4; else if (interleave == TGA_IL_Two) truerow += 2; else truerow++; if (truerow >= h) truerow = ++baserow; } if (mapped) Q_free( ColorMap ); ri.FS_FreeFile( data ); } /* * ================================================================= * * JPEG LOADING * * By Robert 'Heffo' Heffernan * * ================================================================= */ void jpg_null(j_decompress_ptr cinfo) { } boolean jpg_fill_input_buffer(j_decompress_ptr cinfo) { ri.Con_Printf(PRINT_ALL, "Premature end of JPEG data\n"); return 1; } 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; if (cinfo->src->bytes_in_buffer < 0) ri.Con_Printf(PRINT_ALL, "Premature end of JPEG data\n"); } void jpeg_mem_src(j_decompress_ptr cinfo, byte * 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_null; 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_null; cinfo->src->bytes_in_buffer = len; cinfo->src->next_input_byte = mem; } /* * ============== LoadJPG ============== */ void LoadJPG(char *filename, byte ** pic, int *width, int *height) { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; byte *rawdata, *rgbadata, *scanline, *p, *q; int rawsize, i; *pic = NULL; /* Load JPEG file into memory */ rawsize = ri.FS_LoadFile(filename, (void **)&rawdata); if (!rawdata) return; /* Knightmare- check for bad data */ if (rawdata[6] != 'J' || rawdata[7] != 'F' || rawdata[8] != 'I' || rawdata[9] != 'F') { ri.Con_Printf(PRINT_ALL, "Bad jpg file %s\n", filename); ri.FS_FreeFile(rawdata); return; } /* Initialise libJpeg Object */ cinfo.err = jpeg_std_error(&jerr); jpeg_create_decompress(&cinfo); /* Feed JPEG memory into the libJpeg Object */ jpeg_mem_src(&cinfo, rawdata, rawsize); /* Process JPEG header */ jpeg_read_header(&cinfo, true); /* Start Decompression */ jpeg_start_decompress(&cinfo); /* Check Colour Components */ if (cinfo.output_components != 3) { ri.Con_Printf(PRINT_ALL, "Invalid JPEG colour components\n"); jpeg_destroy_decompress(&cinfo); ri.FS_FreeFile(rawdata); return; } /* Allocate Memory for decompressed image */ rgbadata = Q_malloc(cinfo.output_width * cinfo.output_height * 4); if (!rgbadata) { ri.Con_Printf(PRINT_ALL, "Insufficient RAM for JPEG buffer\n"); jpeg_destroy_decompress(&cinfo); ri.FS_FreeFile(rawdata); return; } /* Pass sizes to output */ *width = cinfo.output_width; *height = cinfo.output_height; /* Allocate Scanline buffer */ scanline = Q_malloc(cinfo.output_width * 3); if (!scanline) { ri.Con_Printf(PRINT_ALL, "Insufficient RAM for JPEG scanline buffer\n"); Q_free(rgbadata); jpeg_destroy_decompress(&cinfo); ri.FS_FreeFile(rawdata); return; } /* Read Scanlines, and expand from RGB to RGBA */ q = rgbadata; while (cinfo.output_scanline < cinfo.output_height) { p = scanline; jpeg_read_scanlines(&cinfo, &scanline, 1); for (i = 0; i < cinfo.output_width; i++) { q[0] = p[0]; q[1] = p[1]; q[2] = p[2]; q[3] = 255; p += 3; q += 4; } } /* Free the scanline buffer */ Q_free(scanline); /* Finish Decompression */ jpeg_finish_decompress(&cinfo); /* Destroy JPEG object */ jpeg_destroy_decompress(&cinfo); /* Return the 'rgbadata' */ *pic = rgbadata; } /* * ============================================================= * * PNG STUFF * * ============================================================= */ typedef struct png_s { /* TPng = class(TGraphic) */ char *tmpBuf; int tmpi; long FBgColor; /* DL Background color Added 30/05/2000 */ int FTransparent; /* DL Is this Image Transparent Added 30/05/2000 */ long FRowBytes; /* DL Added 30/05/2000 */ double FGamma; /* DL Added 07/06/2000 */ double FScreenGamma; /* DL Added 07/06/2000 */ char *FRowPtrs; /* DL Changed for consistancy 30/05/2000 */ char *Data; /* property Data: pByte read fData; */ char *Title; char *Author; char *Description; int BitDepth; int BytesPerPixel; int ColorType; unsigned long Height; unsigned long Width; int Interlace; int Compression; int Filter; double LastModified; int Transparent; } png_t; png_t *my_png = 0; unsigned char *pngbytes; void InitializeDemData(void) { long *cvaluep;/* ub */ long y; /* Initialize Data and RowPtrs */ if (my_png->Data) { Q_free(my_png->Data); my_png->Data = 0; } if (my_png->FRowPtrs) { Q_free(my_png->FRowPtrs); my_png->FRowPtrs = 0; } my_png->Data = Q_malloc(my_png->Height * my_png->FRowBytes); /* DL Added 30/5/2000 */ my_png->FRowPtrs = Q_malloc(sizeof(void *) * my_png->Height); if ((my_png->Data) && (my_png->FRowPtrs)) { cvaluep = (long *)my_png->FRowPtrs; for (y = 0; y < my_png->Height; y++) { cvaluep[y] = (long)my_png->Data + (y * (long)my_png->FRowBytes); /* DL Added 08/07/2000 */ } } } void mypng_struct_create(void) { if (my_png) return; my_png = Q_malloc(sizeof(png_t)); my_png->Data = 0; my_png->FRowPtrs = 0; my_png->Height = 0; my_png->Width = 0; my_png->ColorType = PNG_COLOR_TYPE_RGB; my_png->Interlace = PNG_INTERLACE_NONE; my_png->Compression = PNG_COMPRESSION_TYPE_DEFAULT; my_png->Filter = PNG_FILTER_TYPE_DEFAULT; } void mypng_struct_destroy(qboolean keepData) { if (!my_png) return; if (my_png->Data && !keepData) Q_free(my_png->Data); if (my_png->FRowPtrs) Q_free(my_png->FRowPtrs); Q_free(my_png); my_png = 0; } /* * ============================================================= * * fReadData * * ============================================================= */ void PNGAPI fReadData(png_structp png, png_bytep data, png_size_t length) { /* called by pnglib */ int i; for (i = 0; i < length; i++) data[i] = my_png->tmpBuf[my_png->tmpi++]; /* give pnglib a some more bytes */ } /* * ============================================================= * * LoadPNG * * ============================================================= */ void LoadPNG(char *filename, byte ** pic, int *width, int *height) { png_structp png; png_infop pnginfo; byte ioBuffer [8192]; int len; byte *raw; *pic = 0; len = ri.FS_LoadFile(filename, (void **)&raw); if (!raw) { ri.Con_Printf(PRINT_DEVELOPER, "Bad png file %s\n", filename); return; } if (png_sig_cmp(raw, 0, 4)) return; png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); if (!png) return; pnginfo = png_create_info_struct(png); if (!pnginfo) { png_destroy_read_struct(&png, &pnginfo, 0); return; } png_set_sig_bytes(png, 0 /* sizeof( sig ) */ ); mypng_struct_create(); /* creates the my_png struct */ my_png->tmpBuf = (char *)raw; /* buf = whole file content */ my_png->tmpi = 0; png_set_read_fn(png, ioBuffer, fReadData); png_read_info(png, pnginfo); png_get_IHDR(png, pnginfo, &my_png->Width, &my_png->Height, &my_png->BitDepth, &my_png->ColorType, &my_png->Interlace, &my_png->Compression, &my_png->Filter); /* ...removed bgColor code here... */ if (my_png->ColorType == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png); if (my_png->ColorType == PNG_COLOR_TYPE_GRAY && my_png->BitDepth < 8) png_set_gray_1_2_4_to_8(png); /* Add alpha channel if present */ if (png_get_valid(png, pnginfo, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png); /* hax: expand 24bit to 32bit */ if (my_png->BitDepth == 8 && my_png->ColorType == PNG_COLOR_TYPE_RGB) png_set_filler(png, 255, PNG_FILLER_AFTER); if ((my_png->ColorType == PNG_COLOR_TYPE_GRAY) || (my_png->ColorType == PNG_COLOR_TYPE_GRAY_ALPHA)) png_set_gray_to_rgb(png); if (my_png->BitDepth < 8) png_set_expand(png); /* update the info structure */ png_read_update_info(png, pnginfo); my_png->FRowBytes = png_get_rowbytes(png, pnginfo); my_png->BytesPerPixel = png_get_channels(png, pnginfo); /* DL Added 30/08/2000 */ InitializeDemData(); if ((my_png->Data) && (my_png->FRowPtrs)) png_read_image(png, (png_bytepp) my_png->FRowPtrs); png_read_end(png, pnginfo); /* read last information chunks */ png_destroy_read_struct(&png, &pnginfo, 0); /* only load 32 bit by now... */ if (my_png->BitDepth == 8) { *pic = (byte *)my_png->Data; *width = my_png->Width; *height = my_png->Height; } else { ri.Con_Printf(PRINT_DEVELOPER, "Bad png color depth: %s\n", filename); *pic = NULL; Q_free(my_png->Data); } mypng_struct_destroy(true); ri.FS_FreeFile((void *)raw); } /* ==================================================================== IMAGE FLOOD FILLING ==================================================================== */ /* ================= Mod_FloodFillSkin Fill background pixels so mipmapping doesn't have haloes ================= */ typedef struct { short x, y; } floodfill_t; // must be a power of 2 #define FLOODFILL_FIFO_SIZE 0x1000 #define FLOODFILL_FIFO_MASK (FLOODFILL_FIFO_SIZE - 1) #define FLOODFILL_STEP( off, dx, dy ) \ { \ if (pos[off] == fillcolor) \ { \ pos[off] = 255; \ fifo[inpt].x = x + (dx), fifo[inpt].y = y + (dy); \ inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \ } \ else if (pos[off] != 255) fdc = pos[off]; \ } void R_FloodFillSkin( byte *skin, int skinwidth, int skinheight ) { byte fillcolor = *skin; // assume this is the pixel to fill floodfill_t fifo[FLOODFILL_FIFO_SIZE]; int inpt = 0, outpt = 0; int filledcolor = -1; int i; if (filledcolor == -1) { filledcolor = 0; // attempt to find opaque black for (i = 0; i < 256; ++i) if (d_8to24table[i] == (255 << 0)) // alpha 1.0 { filledcolor = i; break; } } // can't fill to filled color or to transparent color (used as visited marker) if ((fillcolor == filledcolor) || (fillcolor == 255)) { //printf( "not filling skin from %d to %d\n", fillcolor, filledcolor ); return; } fifo[inpt].x = 0, fifo[inpt].y = 0; inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; while (outpt != inpt) { int x = fifo[outpt].x, y = fifo[outpt].y; int fdc = filledcolor; byte *pos = &skin[x + skinwidth * y]; outpt = (outpt + 1) & FLOODFILL_FIFO_MASK; if (x > 0) FLOODFILL_STEP( -1, -1, 0 ); if (x < skinwidth - 1) FLOODFILL_STEP( 1, 1, 0 ); if (y > 0) FLOODFILL_STEP( -skinwidth, 0, -1 ); if (y < skinheight - 1) FLOODFILL_STEP( skinwidth, 0, 1 ); skin[x + skinwidth * y] = fdc; } } //======================================================= /* ================ GL_ResampleTexture ================ */ void GL_ResampleTexture (unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight) { int i, j; unsigned *inrow, *inrow2; unsigned frac, fracstep; unsigned p1[1024], p2[1024]; byte *pix1, *pix2, *pix3, *pix4; fracstep = inwidth*0x10000/outwidth; frac = fracstep>>2; for (i=0 ; i>16); frac += fracstep; } frac = 3*(fracstep>>2); for (i=0 ; i>16); frac += fracstep; } for (i=0 ; i> 1; for (j=0 ; j>2; ((byte *)(out+j))[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2; ((byte *)(out+j))[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2; ((byte *)(out+j))[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2; } } } /* ================ GL_LightScaleTexture Scale up the pixel values in a texture to increase the lighting range ================ */ void GL_LightScaleTexture (unsigned *in, int inwidth, int inheight, qboolean only_gamma ) { if ( only_gamma ) { int i, c; byte *p; p = (byte *)in; c = inwidth*inheight; for (i=0 ; i>= 1; out = in; for (i=0 ; i>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; } } } void desaturate_texture(unsigned *udata, int width, int height) { // jitsaturation int i, size; float r, g, b, v, s; unsigned char *data; data = (unsigned char *)udata; s = gl_lightmap_texture_saturation->value; size = width * height * 4; for (i = 0; i < size; i += 4) { r = data[i]; g = data[i + 1]; b = data[i + 2]; v = r * 0.30 + g * 0.59 + b * 0.11; data[i] = (1 - s) * v + s * r; data[i + 1] = (1 - s) * v + s * g; data[i + 2] = (1 - s) * v + s * b; } } /* =============== GL_Upload32 Returns has_alpha =============== */ void GL_BuildPalettedTexture( unsigned char *paletted_texture, unsigned char *scaled, int scaled_width, int scaled_height ) { int i; for ( i = 0; i < scaled_width * scaled_height; i++ ) { unsigned int r, g, b, c; r = ( scaled[0] >> 3 ) & 31; g = ( scaled[1] >> 2 ) & 63; b = ( scaled[2] >> 3 ) & 31; c = r | ( g << 5 ) | ( b << 11 ); paletted_texture[i] = gl_state.d_16to8table[c]; scaled += 4; } } int upload_width, upload_height; qboolean uploaded_paletted = false; int nearest_power_of_2(int size) { int i = 2; /* NeVo - infinite loop bug-fix */ if (size == 1) return size; while (1) { i <<= 1; if (size == i) return i; if (size > i && size < (i <<1)) { if (size >= ((i+(i<<1))/2)) return i<<1; else return i; } }; } #if defined(USE_GLU) qboolean GL_Upload32 (unsigned *data, int width, int height, qboolean mipmap) { int samples; unsigned *scaled; int scaled_width, scaled_height; int i, c; byte *scan; int comp; uploaded_paletted = false; // scan the texture for any non-255 alpha c = width*height; scan = ((byte *)data) + 3; samples = gl_solid_format; for (i=0 ; i max_size) scaled_width = max_size; if (scaled_height > max_size) scaled_height = max_size; if (scaled_width < 2) scaled_width = 2; if (scaled_height < 2) scaled_height = 2; } if (scaled_width != width || scaled_height != height) { scaled=Q_malloc((scaled_width * scaled_height) * 4); GL_ResampleTexture(data,width,height,scaled,scaled_width,scaled_height); } else { scaled_width =width; scaled_height =height; scaled =data; } if (gl_lightmap_texture_saturation->value < 1) // jitsaturation desaturate_texture(scaled, scaled_width, scaled_height); if (mipmap) { if (brightenTexture) GL_LightScaleTexture(scaled, scaled_width, scaled_height, !mipmap); if (gl_state.sgis_mipmap) { qglTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, true); qglTexImage2D(GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); } else gluBuild2DMipmaps(GL_TEXTURE_2D, samples, scaled_width, scaled_height, GL_RGBA, GL_UNSIGNED_BYTE, scaled); } else { if (brightenTexture) GL_LightScaleTexture(scaled, scaled_width, scaled_height, !mipmap); qglTexImage2D(GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); } if (scaled_width != width || scaled_height != height) Q_free(scaled); upload_width=scaled_width; upload_height = scaled_height; if (mipmap) { qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); if (gl_anisotropy->value) // jitanisotropy qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_anisotropy->value); else qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } else { qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } return (samples == gl_alpha_format); } #else qboolean GL_Upload32 (unsigned *data, int width, int height, qboolean mipmap) { int samples; unsigned *scaled; int scaled_width, scaled_height; int i, c; byte *scan; int comp; GLint max_size; qboolean sharp = false; uploaded_paletted = false; for (scaled_width = 1; scaled_width < width; scaled_width <<= 1); for (scaled_height = 1; scaled_height < height; scaled_height <<= 1); if (gl_round_down->value && scaled_width > width && mipmap) scaled_width >>= 1; if (gl_round_down->value && scaled_height > height && mipmap) scaled_height >>= 1; // let people sample down the world textures for speed if (gl_picmip->value > 4.0f) gl_picmip->value = 4.0f; // jit if (mipmap) { scaled_width >>= (int)gl_picmip->value; scaled_height >>= (int)gl_picmip->value; } // find max size card can handle qglGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_size); if (scaled_width > max_size) scaled_width = max_size; if (scaled_height > max_size) scaled_height = max_size; if (scaled_width <= 0) scaled_width = 1; if (scaled_height <= 0) scaled_height = 1; upload_width = scaled_width; upload_height = scaled_height; // scan the texture for any non-255 alpha c = width*height; scan = ((byte*)data) + 3; samples = gl_solid_format; for (i = 0; i < c; i++, scan += 4) { if (*scan != 255) { samples = gl_alpha_format; break; } } //Heffo - ARB Texture Compression qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST); if (samples == gl_solid_format) comp = (gl_state.texture_compression) ? GL_COMPRESSED_RGB_ARB : gl_tex_solid_format; else if (samples == gl_alpha_format) comp = (gl_state.texture_compression) ? GL_COMPRESSED_RGBA_ARB : gl_tex_alpha_format; else { ri.Con_Printf (PRINT_ALL,"Unknown number of texture components %i\n",samples); comp = samples; } // find sizes to scale to { int max_size; qglGetIntegerv(GL_MAX_TEXTURE_SIZE,&max_size); scaled_width = nearest_power_of_2(width); scaled_height = nearest_power_of_2(height); if (scaled_width > max_size) scaled_width = max_size; if (scaled_height > max_size) scaled_height = max_size; if (scaled_width < 2) scaled_width = 2; if (scaled_height < 2) scaled_height = 2; } if (scaled_width == width && scaled_height == height) scaled = data; else { scaled = Q_malloc(scaled_width*scaled_height*4); GL_ResampleTexture(data, width, height, scaled, scaled_width, scaled_height); } // jitsaturation if (gl_lightmap_texture_saturation->value < 1) desaturate_texture(scaled, scaled_width, scaled_height); if (mipmap) { if (brightenTexture) GL_LightScaleTexture(scaled, scaled_width, scaled_height, !mipmap); if (gl_state.sgis_mipmap) { qglTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); qglTexImage2D(GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); } else { int miplevel = 0; qglTexImage2D(GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); while (scaled_width > 1 || scaled_height > 1) { GL_MipMap((byte *)scaled, scaled_width, scaled_height); scaled_width >>= 1; scaled_height >>= 1; if (scaled_width < 1) scaled_width = 1; if (scaled_height < 1) scaled_height = 1; miplevel++; qglTexImage2D(GL_TEXTURE_2D, miplevel, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); } } qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); // jitanisotropy if (gl_anisotropy->value) qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_anisotropy->value); else qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } else { if (brightenTexture) GL_LightScaleTexture(scaled, scaled_width, scaled_height, !mipmap); if (gl_state.sgis_mipmap) qglTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_FALSE); qglTexImage2D(GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, sharp ? GL_NEAREST : gl_filter_max); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, sharp ? GL_NEAREST : gl_filter_max); } { qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); } if (upload_width != width || upload_height != height) Q_free(scaled); return (samples == gl_alpha_format); } #endif qboolean GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean is_sky ) { unsigned trans[512*256]; int i, s; int p; s = width*height; if (s > sizeof(trans)/4) ri.Sys_Error (ERR_DROP, "GL_Upload8: too large"); { for (i=0 ; i width && data[i-width] != 255) p = data[i-width]; else if (i < s-width && data[i+width] != 255) p = data[i+width]; 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 ((byte *)&trans[i])[0] = ((byte *)&d_8to24table[p])[0]; ((byte *)&trans[i])[1] = ((byte *)&d_8to24table[p])[1]; ((byte *)&trans[i])[2] = ((byte *)&d_8to24table[p])[2]; } } return GL_Upload32 (trans, width, height, mipmap); } } /* ================ GL_LoadPic This is also used as an entry point for the generated r_notexture ================ */ image_t *GL_LoadPic (char *name, byte *pic, int width, int height, imagetype_t type, int bits) { image_t *image; int i; miptex_t *mt; int len; char s[128]; // find a free image_t for (i=0, image=gltextures ; itexnum) break; } if (i == numgltextures) { if (numgltextures == MAX_GLTEXTURES) ri.Sys_Error (ERR_DROP, "MAX_GLTEXTURES"); numgltextures++; } image = &gltextures[i]; if (strlen(name) >= sizeof(image->name)) ri.Sys_Error (ERR_DROP, "Draw_LoadPic: \"%s\" is too long", name); Q_strncpyz(image->name, name, sizeof(image->name)); image->registration_sequence = registration_sequence; image->width = width; image->height = height; image->type = type; image->replace_scale = 1.0; if (type == it_skin && bits == 8) R_FloodFillSkin(pic, width, height); // NiceAss: Nexus's image replacement scaling code len = strlen(name); Q_strncpyz(s, name, sizeof(s)); if( !strcmp(s + len - 4, ".png") || !strcmp(s + len - 4, ".tga") || !strcmp(s+len-4, ".jpg")) { s[len - 3] = 'w'; s[len - 2] = 'a'; s[len - 1] = 'l'; ri.FS_LoadFile (s, (void **) &mt); //load .wal file if (mt) { image->width = LittleLong(mt->width); //grab size from wal image->height = LittleLong(mt->height); ri.FS_FreeFile((void *) mt); //free the picture } else { // if it's replacing a PCX image byte *pic, *palette; int pcxwidth , pcxheight; s[len - 3] = 'p'; s[len - 2] = 'c'; s[len - 1] = 'x'; // replace extension LoadPCX(s, &pic, &palette, &pcxwidth, &pcxheight); // load .pcx file if (pcxwidth > 0 && pcxheight > 0) { image->replace_scale = ((float)pcxwidth / image->width + (float)pcxheight / image->height) * 0.5; if (image->replace_scale == 0.0) image->replace_scale = 1.0; image->replace_scale = min(image->replace_scale, 1.0); } if (pic) Q_free(pic); if (palette) Q_free(palette); } } if (type == it_skin && bits == 8) R_FloodFillSkin(pic, width, height); // load little pics into the scrap if (image->type == it_pic && bits == 8 && image->width < 64 && image->height < 64) { int x, y; int i, j, k; int texnum; texnum = Scrap_AllocBlock (image->width, image->height, &x, &y); if (texnum == -1) goto nonscrap; scrap_dirty = true; // copy the texels into the scrap block k = 0; for (i=0 ; iheight ; i++) for (j=0 ; jwidth ; j++, k++) scrap_texels[texnum][(y+i)*BLOCK_WIDTH + x + j] = pic[k]; image->texnum = TEXNUM_SCRAPS + texnum; image->scrap = true; image->has_alpha = true; image->sl = (x+0.01)/(float)BLOCK_WIDTH; image->sh = (x+image->width-0.01)/(float)BLOCK_WIDTH; image->tl = (y+0.01)/(float)BLOCK_WIDTH; image->th = (y+image->height-0.01)/(float)BLOCK_WIDTH; } else { nonscrap: image->scrap = false; image->texnum = TEXNUM_IMAGES + (image - gltextures); GL_Bind(image->texnum); if (bits == 8) image->has_alpha = GL_Upload8 (pic, width, height, (image->type != it_pic && image->type != it_sky), image->type == it_sky ); else image->has_alpha = GL_Upload32 ((unsigned *)pic, width, height, (image->type != it_pic && image->type != it_sky) ); image->upload_width = upload_width; // after power of 2 and scales image->upload_height = upload_height; image->paletted = uploaded_paletted; image->sl = 0; image->sh = 1; image->tl = 0; image->th = 1; } return image; } /* ================ GL_LoadWal ================ */ image_t *GL_LoadWal (char *name) { miptex_t *mt; int width, height, ofs; image_t *image; ri.FS_LoadFile (name, (void **)&mt); if (!mt) { ri.Con_Printf (PRINT_ALL, "GL_FindImage: can't load %s\n", name); return r_notexture; } width = LittleLong (mt->width); height = LittleLong (mt->height); ofs = LittleLong (mt->offsets[0]); image = GL_LoadPic (name, (byte *)mt + ofs, width, height, it_wall, 8); ri.FS_FreeFile ((void *)mt); return image; } /* =============== GL_FindImage Finds or loads the given image =============== */ image_t *GL_FindImage (char *name, imagetype_t type) { image_t *image; int i, len; byte *pic, *palette; int width, height; #if defined (__unix__) char *ptr; #endif if (!name) return NULL; // ri.Sys_Error (ERR_DROP, "GL_FindImage: NULL name"); len = strlen(name); if (len<5) return NULL; // ri.Sys_Error (ERR_DROP, "GL_FindImage: bad name: %s", name); #if defined (__unix__) // fix backslashes while ((ptr=strchr(name,'\\'))) { *ptr = '/'; } #endif // look for it for (i=0, image=gltextures ; iname)) { image->registration_sequence = registration_sequence; return image; } } // // load the pic from disk // pic = NULL; palette = NULL; //Knightmare- MrG's automatic JPG & TGA loading // search for TGAs or JPGs to replace .pcx and .wal images if (!strcmp(name+len-4, ".pcx") || !strcmp(name+len-4, ".wal")) { char s[128]; strcpy(s,name); s[len-3]='p'; s[len-2]='n'; s[len-1]='g'; image = GL_FindImage(s,type); if (image) return image; s[len-3]='t'; s[len-2]='g'; s[len-1]='a'; image = GL_FindImage(s,type); if (image) return image; //Knightmare- if no TGA, check for JPG s[len-3]='j'; s[len-2]='p'; s[len-1]='g'; image = GL_FindImage(s,type); if (image) return image; } if (!strcmp(name+len-4, ".pcx")) { LoadPCX (name, &pic, &palette, &width, &height); if (!pic) return NULL; // ri.Sys_Error (ERR_DROP, "GL_FindImage: can't load %s", name); image = GL_LoadPic (name, pic, width, height, type, 8); } else if (!strcmp(name+len-4, ".wal")) { image = GL_LoadWal (name); } else if (!strcmp(name + len - 4, ".png")) { LoadPNG(name, &pic, &width, &height); if (!pic) return NULL; // ri.Sys_Error (ERR_DROP, "GL_FindImage: can't load %s", name); image = GL_LoadPic(name, pic, width, height, type, 32); } else if (!strcmp(name+len-4, ".tga")) { LoadTGA (name, &pic, &width, &height); if (!pic) return NULL; // ri.Sys_Error (ERR_DROP, "GL_FindImage: can't load %s", name); image = GL_LoadPic (name, pic, width, height, type, 32); } else if (!strcmp(name + len - 4, ".jpg")) { LoadJPG(name, &pic, &width, &height); if (!pic) return NULL; // ri.Sys_Error (ERR_DROP, "GL_FindImage: can't load %s", name); image = GL_LoadPic(name, pic, width, height, type, 32); } else return NULL; // ri.Sys_Error (ERR_DROP, "GL_FindImage: bad extension on: %s", name); if (pic) Q_free(pic); if (palette) Q_free(palette); return image; } /* =============== R_RegisterSkin =============== */ struct image_s *R_RegisterSkin (char *name) { return GL_FindImage (name, it_skin); } /* ================ GL_FreeUnusedImages Any image that was not touched on this registration sequence will be freed. ================ */ void GL_FreeUnusedImages (void) { int i; image_t *image; // never free r_notexture or particle texture r_notexture->registration_sequence = registration_sequence; #if defined(PARTICLESYSTEM) r_particlebeam->registration_sequence = registration_sequence; for (i=0; iregistration_sequence = registration_sequence; #else r_particletexture->registration_sequence = registration_sequence; #endif // MH - detail textures r_detailtexture->registration_sequence = registration_sequence; r_caustictexture->registration_sequence = registration_sequence; r_shelltexture->registration_sequence = registration_sequence; r_radarmap->registration_sequence = registration_sequence; r_around->registration_sequence = registration_sequence; for (i=0, image=gltextures ; iregistration_sequence == registration_sequence) continue; // used this sequence if (!image->registration_sequence) continue; // free image_t slot if (image->type == it_pic) continue; // don't free pics // free it qglDeleteTextures (1, &image->texnum); memset (image, 0, sizeof(*image)); } } #if !defined(PARTICLESYSTEM) float d_8to24tablef[256][3]; const byte default_pal[768] = { 0,0,0,15,15,15,31,31,31,47,47,47,63,63,63,75,75,75,91,91,91,107,107,107,123,123,123,139,139,139,155,155, 155,171,171,171,187,187,187,203,203,203,219,219,219,235,235,235,99,75,35,91,67,31,83,63,31,79,59,27,71,55, 27,63,47,23,59,43,23,51,39,19,47,35,19,43,31,19,39,27,15,35,23,15,27,19,11,23,15,11,19,15,7,15,11,7,95,95, 111,91,91,103,91,83,95,87,79,91,83,75,83,79,71,75,71,63,67,63,59,59,59,55,55,51,47,47,47,43,43,39,39,39,35, 35,35,27,27,27,23,23,23,19,19,19,143,119,83,123,99,67,115,91,59,103,79,47,207,151,75,167,123,59,139,103,47, 111,83,39,235,159,39,203,139,35,175,119,31,147,99,27,119,79,23,91,59,15,63,39,11,35,23,7,167,59,43,159,47, 35,151,43,27,139,39,19,127,31,15,115,23,11,103,23,7,87,19,0,75,15,0,67,15,0,59,15,0,51,11,0,43,11,0,35,11, 0,27,7,0,19,7,0,123,95,75,115,87,67,107,83,63,103,79,59,95,71,55,87,67,51,83,63,47,75,55,43,67,51,39,63,47, 35,55,39,27,47,35,23,39,27,19,31,23,15,23,15,11,15,11,7,111,59,23,95,55,23,83,47,23,67,43,23,55,35,19,39, 27,15,27,19,11,15,11,7,179,91,79,191,123,111,203,155,147,215,187,183,203,215,223,179,199,211,159,183,195, 135,167,183,115,151,167,91,135,155,71,119,139,47,103,127,23,83,111,19,75,103,15,67,91,11,63,83,7,55,75,7,47, 63,7,39,51,0,31,43,0,23,31,0,15,19,0,7,11,0,0,0,139,87,87,131,79,79,123,71,71,115,67,67,107,59,59,99,51,51, 91,47,47,87,43,43,75,35,35,63,31,31,51,27,27,43,19,19,31,15,15,19,11,11,11,7,7,0,0,0,151,159,123,143,151,115, 135,139,107,127,131,99,119,123,95,115,115,87,107,107,79,99,99,71,91,91,67,79,79,59,67,67,51,55,55,43,47,47, 35,35,35,27,23,23,19,15,15,11,159,75,63,147,67,55,139,59,47,127,55,39,119,47,35,107,43,27,99,35,23,87,31,19, 79,27,15,67,23,11,55,19,11,43,15,7,31,11,7,23,7,0,11,0,0,0,0,0,119,123,207,111,115,195,103,107,183,99,99,167, 91,91,155,83,87,143,75,79,127,71,71,115,63,63,103,55,55,87,47,47,75,39,39,63,35,31,47,27,23,35,19,15,23,11,7, 7,155,171,123,143,159,111,135,151,99,123,139,87,115,131,75,103,119,67,95,111,59,87,103,51,75,91,39,63,79,27, 55,67,19,47,59,11,35,47,7,27,35,0,19,23,0,11,15,0,0,255,0,35,231,15,63,211,27,83,187,39,95,167,47,95,143,51, 95,123,51,255,255,255,255,255,211,255,255,167,255,255,127,255,255,83,255,255,39,255,235,31,255,215,23,255, 191,15,255,171,7,255,147,0,239,127,0,227,107,0,211,87,0,199,71,0,183,59,0,171,43,0,155,31,0,143,23,0,127,15, 0,115,7,0,95,0,0,71,0,0,47,0,0,27,0,0,239,0,0,55,55,255,255,0,0,0,0,255,43,43,35,27,27,23,19,19,15,235,151, 127,195,115,83,159,87,51,123,63,27,235,211,199,199,171,155,167,139,119,135,107,87,159,91,83 }; #endif /* =============== Draw_GetPalette =============== */ int Draw_GetPalette (void) { int i; int r, g, b; unsigned v; byte *pic, *pal; int width, height; // get the palette LoadPCX ("pics/colormap.pcx", &pic, &pal, &width, &height); if (!pal) ri.Sys_Error (ERR_FATAL, "Couldn't load pics/colormap.pcx"); for (i=0 ; i<256 ; i++) { r = pal[i*3+0]; g = pal[i*3+1]; b = pal[i*3+2]; v = (255<<24) + (r<<0) + (g<<8) + (b<<16); d_8to24table[i] = LittleLong(v); #if !defined(PARTICLESYSTEM) d_8to24tablef[i][0] = r * 0.003921568627450980392156862745098; d_8to24tablef[i][1] = g * 0.003921568627450980392156862745098; d_8to24tablef[i][2] = b * 0.003921568627450980392156862745098; #endif } d_8to24table[255] &= LittleLong(0xffffff); // 255 is transparent Q_free (pic); Q_free (pal); return 0; } /* =============== GL_InitImages =============== */ void GL_InitImages (void) { int i, j; float g = vid_gamma->value; registration_sequence = 1; // init intensity conversions /* Vic */ if ( gl_config.mtexcombine ) intensity = ri.Cvar_Get ("intensity", "1.5", CVAR_ARCHIVE); else /* Vic */ intensity = ri.Cvar_Get ("intensity", "2", 0); if ( intensity->value <= 1 ) ri.Cvar_Set( "intensity", "1" ); gl_state.inverse_intensity = 1 / intensity->value; Draw_GetPalette (); if ( qglColorTableEXT ) { ri.FS_LoadFile( "pics/16to8.dat", (void **)&gl_state.d_16to8table ); if ( !gl_state.d_16to8table ) ri.Sys_Error( ERR_FATAL, "Couldn't load pics/16to8.pcx"); } for ( i = 0; i < 256; i++ ) { if ( g == 1 ) gammatable[i] = i; else { float inf; inf = 255 * pow ( (i+0.5)*DIV255 , g ) + 0.5; if (inf < 0) inf = 0; if (inf > 255) inf = 255; gammatable[i] = inf; } } for (i=0 ; i<256 ; i++) { j = i*intensity->value; if (j > 255) j = 255; intensitytable[i] = j; } } /* =============== GL_ShutdownImages =============== */ void GL_ShutdownImages (void) { int i; image_t *image; for (i=0, image=gltextures ; iregistration_sequence) continue; // free image_t slot // free it qglDeleteTextures (1, &image->texnum); memset (image, 0, sizeof(*image)); } }