/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "qdata.h" #ifdef _WIN32 #include #endif #include #if 1 extern char *g_outputDir; #endif // _QDATA char mip_prefix[1024]; // directory to dump the textures in qboolean colormap_issued; byte colormap_palette[768]; unsigned total_x = 0; unsigned total_y = 0; unsigned total_textures = 0; #define MAX_IMAGE_SIZE 512 #if 0 /* ============== RemapZero Replaces all 0 bytes in an image with the closest palette entry. This is because NT won't let us change index 0, so any palette animation leaves those pixels untouched. ============== */ void RemapZero( byte *pixels, byte *palette, int width, int height ){ int i, c; int alt_zero; int value, best; alt_zero = 0; best = 9999999; for ( i = 1 ; i < 255 ; i++ ) { value = palette[i * 3 + 0] + palette[i * 3 + 1] + palette[i * 3 + 2]; if ( value < best ) { best = value; alt_zero = i; } } c = width * height; for ( i = 0 ; i < c ; i++ ) if ( pixels[i] == 0 ) { pixels[i] = alt_zero; } } #endif // ******************************************************************** // ** Mip Map Pre-Processing Routines // ******************************************************************** #define intensity_value 1 static unsigned image_pal[256]; #define MAX_LAST 25 long palette_r[256], palette_g[256], palette_b[256]; long last_r[MAX_LAST],last_g[MAX_LAST],last_b[MAX_LAST], last_i[MAX_LAST], last_place; long cached; void PrepareConvert( unsigned *palette ){ int i; for ( i = 0; i < 256; i++ ) { palette_r[i] = ( palette[i] & 0x00ff0000 ) >> 16; palette_g[i] = ( palette[i] & 0x0000ff00 ) >> 8; palette_b[i] = ( palette[i] & 0x000000ff ); } for ( i = 0; i < MAX_LAST; i++ ) last_r[i] = -1; last_place = -1; } int ConvertTrueColorToPal( unsigned r, unsigned g, unsigned b ){ int i; long min_dist; int min_index; long dist; long dr, dg, db, biggest_delta; for ( i = 0; i < MAX_LAST; i++ ) if ( r == last_r[i] && g == last_g[i] && b == last_b[i] ) { cached++; return last_i[i]; } min_dist = 256 * 256 + 256 * 256 + 256 * 256; biggest_delta = 256 * 256; min_index = 0; for ( i = 0; i < 256; i++ ) { dr = abs( palette_r[i] - r ); if ( dr > biggest_delta ) { continue; } dg = abs( palette_g[i] - g ); if ( dg > biggest_delta ) { continue; } db = abs( palette_b[i] - b ); if ( db > biggest_delta ) { continue; } dist = dr * dr + dg * dg + db * db; if ( dist < min_dist ) { min_dist = dist; min_index = i; if ( min_dist == 0 ) { break; } dist = dr; if ( dg > dist ) { dist = dg; } if ( db > dist ) { dist = db; } if ( dist < biggest_delta ) { biggest_delta = dist; } } } last_place++; if ( last_place >= MAX_LAST ) { last_place = 0; } last_r[last_place] = r; last_g[last_place] = g; last_b[last_place] = b; last_i[last_place] = min_index; return min_index; } void GL_ResampleTexture8P( byte *in, int inwidth, int inheight, byte *out, int outwidth, int outheight, palette_t *palette ){ int i, j; byte *inrow, *inrow2; unsigned frac, fracstep; unsigned p1[1024], p2[1024], *p1p, *p2p; palette_t *c1,*c2,*c3,*c4; unsigned r,g,b; fracstep = inwidth * 0x10000 / outwidth; frac = fracstep >> 2; for ( i = 0 ; i < outwidth ; i++ ) { p1[i] = frac >> 16; frac += fracstep; } frac = 3 * ( fracstep >> 2 ); for ( i = 0 ; i < outwidth ; i++ ) { p2[i] = frac >> 16; frac += fracstep; } cached = 0; 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 ); p1p = p1; p2p = p2; for ( j = 0 ; j < outwidth ; j++ ) { c1 = &palette[*( (byte *)inrow + ( *p1p ) )]; c2 = &palette[*( (byte *)inrow + ( *p2p ) )]; c3 = &palette[*( (byte *)inrow2 + ( *p1p++ ) )]; c4 = &palette[*( (byte *)inrow2 + ( *p2p++ ) )]; r = ( (unsigned)c1->r + (unsigned)c2->r + (unsigned)c3->r + (unsigned)c4->r ) >> 2; g = ( (unsigned)c1->g + (unsigned)c2->g + (unsigned)c3->g + (unsigned)c4->g ) >> 2; b = ( (unsigned)c1->b + (unsigned)c2->b + (unsigned)c3->b + (unsigned)c4->b ) >> 2; *out++ = ConvertTrueColorToPal( r,g,b ); } } } void GL_MipMap8P( byte *out, byte *in, int width, int height, palette_t *palette ){ int i, j; palette_t *c1,*c2,*c3,*c4; unsigned r,g,b; cached = 0; memset( out, 0, 256 * 256 ); width <<= 1; height <<= 1; for ( i = 0; i < height; i += 2, in += width ) { for ( j = 0; j < width; j += 2 ) { c1 = &palette[in[0]]; c3 = &palette[in[width]]; in++; c2 = &palette[in[0]]; c4 = &palette[in[width]]; in++; r = ( (unsigned)c1->r + (unsigned)c2->r + (unsigned)c3->r + (unsigned)c4->r ) >> 2; g = ( (unsigned)c1->g + (unsigned)c2->g + (unsigned)c3->g + (unsigned)c4->g ) >> 2; b = ( (unsigned)c1->b + (unsigned)c2->b + (unsigned)c3->b + (unsigned)c4->b ) >> 2; *out++ = ConvertTrueColorToPal( r, g, b ); } } } miptex_t *CreateMip( byte *data, unsigned width, unsigned height, byte *palette, int *FinalSize, qboolean mip ){ int scaled_width, scaled_height; int i,j,r,g,b; byte intensitytable[256]; byte scaled[256 * 256]; byte out[256 * 256]; int miplevel; miptex_t *mp; byte *pos; int size; for ( i = 0 ; i < 256 ; i++ ) { j = i * intensity_value; if ( j > 255 ) { j = 255; } intensitytable[i] = j; } for ( scaled_width = 1 ; scaled_width < width ; scaled_width <<= 1 ) ; if ( 1 && scaled_width > width && 1 ) { scaled_width >>= 1; } for ( scaled_height = 1 ; scaled_height < height ; scaled_height <<= 1 ) ; if ( 1 && scaled_height > height && 1 ) { scaled_height >>= 1; } // don't ever bother with >256 textures if ( scaled_width > 256 ) { scaled_width = 256; } if ( scaled_height > 256 ) { scaled_height = 256; } if ( scaled_width < 1 ) { scaled_width = 1; } if ( scaled_height < 1 ) { scaled_height = 1; } size = sizeof( *mp ) + ( scaled_width * scaled_height * 3 ); mp = (miptex_t *)SafeMalloc( size, "CreateMip" ); memset( mp,0,size ); mp->version = MIP_VERSION; for ( i = j = 0; i < 256; i++,j += 3 ) { mp->palette[i].r = r = intensitytable[palette[j]]; mp->palette[i].g = g = intensitytable[palette[j + 1]]; mp->palette[i].b = b = intensitytable[palette[j + 2]]; image_pal[i] = 0xff000000 | ( r << 16 ) | ( g << 8 ) | ( b ); } PrepareConvert( image_pal ); if ( scaled_width == width && scaled_height == height ) { memcpy( scaled, data, width * height ); } else{ GL_ResampleTexture8P( data, width, height, scaled, scaled_width, scaled_height, mp->palette ); } pos = (byte *)( mp + 1 ); miplevel = 0; while ( ( scaled_width >= 1 || scaled_height >= 1 ) && ( miplevel <= MIPLEVELS - 1 ) && ( !miplevel || mip ) ) { if ( scaled_width < 1 ) { scaled_width = 1; } if ( scaled_height < 1 ) { scaled_height = 1; } if ( miplevel > 0 ) { GL_MipMap8P( out, (byte *)scaled, scaled_width, scaled_height, mp->palette ); } else{ memcpy( out, scaled, 256 * 256 ); } mp->width[miplevel] = scaled_width; mp->height[miplevel] = scaled_height; mp->offsets[miplevel] = pos - ( (byte *)( mp ) ); memcpy( pos, out, scaled_width * scaled_height ); memcpy( scaled, out, 256 * 256 ); pos += scaled_width * scaled_height; scaled_width >>= 1; scaled_height >>= 1; miplevel++; } *FinalSize = pos - ( (byte *)( mp ) ); return mp; } 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 < 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 ); frac = fracstep >> 1; for ( j = 0 ; j < outwidth ; j++ ) { pix1 = (byte *)inrow + p1[j]; pix2 = (byte *)inrow + p2[j]; pix3 = (byte *)inrow2 + p1[j]; pix4 = (byte *)inrow2 + p2[j]; ( (byte *)( out + j ) )[0] = ( pix1[0] + pix2[0] + pix3[0] + pix4[0] ) >> 2; ( (byte *)( out + j ) )[1] = ( pix1[1] + pix2[1] + pix3[1] + pix4[1] ) >> 2; ( (byte *)( out + j ) )[2] = ( pix1[2] + pix2[2] + pix3[2] + pix4[2] ) >> 2; ( (byte *)( out + j ) )[3] = ( pix1[3] + pix2[3] + pix3[3] + pix4[3] ) >> 2; } } } void GL_MipMap( byte *out, byte *in, int width, int height ){ int i, j; width <<= 3; height <<= 1; 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; } } } miptex32_t *CreateMip32( unsigned *data, unsigned width, unsigned height, int *FinalSize, qboolean mip ){ int scaled_width, scaled_height; unsigned scaled[MAX_IMAGE_SIZE * MAX_IMAGE_SIZE]; unsigned out[MAX_IMAGE_SIZE * MAX_IMAGE_SIZE]; int miplevel; miptex32_t *mp; byte *pos; int size; paletteRGBA_t *test; for ( scaled_width = 1 ; scaled_width < width ; scaled_width <<= 1 ) ; if ( 1 && scaled_width > width && 1 ) { scaled_width >>= 1; } for ( scaled_height = 1 ; scaled_height < height ; scaled_height <<= 1 ) ; if ( 1 && scaled_height > height && 1 ) { scaled_height >>= 1; } // don't ever bother with >256 textures if ( scaled_width > MAX_IMAGE_SIZE ) { scaled_width = MAX_IMAGE_SIZE; } if ( scaled_height > MAX_IMAGE_SIZE ) { scaled_height = MAX_IMAGE_SIZE; } if ( scaled_width < 1 ) { scaled_width = 1; } if ( scaled_height < 1 ) { scaled_height = 1; } size = sizeof( *mp ) + ( scaled_width * scaled_height * 3 * 4 ); mp = (miptex32_t *)SafeMalloc( size, "CreateMip" ); memset( mp,0,size ); mp->version = MIP32_VERSION; size = width * height; test = (paletteRGBA_t *)data; while ( size ) { if ( test->a != 255 ) { mp->flags |= LittleLong( SURF_ALPHA_TEXTURE ); break; } size--; test++; } if ( scaled_width == width && scaled_height == height ) { memcpy( scaled, data, width * height * 4 ); } else{ GL_ResampleTexture( data, width, height, scaled, scaled_width, scaled_height ); } pos = (byte *)( mp + 1 ); miplevel = 0; while ( ( scaled_width >= 1 || scaled_height >= 1 ) && ( miplevel <= MIPLEVELS - 1 ) && ( !miplevel || mip ) ) { if ( scaled_width < 1 ) { scaled_width = 1; } if ( scaled_height < 1 ) { scaled_height = 1; } if ( miplevel > 0 ) { GL_MipMap( (byte *)out, (byte *)scaled, scaled_width, scaled_height ); } else { memcpy( out, scaled, MAX_IMAGE_SIZE * MAX_IMAGE_SIZE * 4 ); } mp->width[miplevel] = scaled_width; mp->height[miplevel] = scaled_height; mp->offsets[miplevel] = pos - ( (byte *)( mp ) ); memcpy( pos, out, scaled_width * scaled_height * 4 ); memcpy( scaled, out, MAX_IMAGE_SIZE * MAX_IMAGE_SIZE * 4 ); pos += scaled_width * scaled_height * 4; scaled_width >>= 1; scaled_height >>= 1; miplevel++; } *FinalSize = pos - ( (byte *)( mp ) ); return mp; } /* ============== Cmd_Grab $grab filename x y width height ============== */ void Cmd_Grab( void ){ int xl,yl,w,h,y; byte *cropped; char savename[1024]; char dest[1024]; GetScriptToken( false ); if ( token[0] == '/' || token[0] == '\\' ) { sprintf( savename, "%s%s.pcx", gamedir, token + 1 ); } else{ sprintf( savename, "%spics/%s.pcx", gamedir, token ); } if ( g_release ) { if ( token[0] == '/' || token[0] == '\\' ) { sprintf( dest, "%s.pcx", token + 1 ); } else{ sprintf( dest, "pics/%s.pcx", token ); } ReleaseFile( dest ); return; } GetScriptToken( false ); xl = atoi( token ); GetScriptToken( false ); yl = atoi( token ); GetScriptToken( false ); w = atoi( token ); GetScriptToken( false ); h = atoi( token ); if ( xl < 0 || yl < 0 || w < 0 || h < 0 || xl + w > byteimagewidth || yl + h > byteimageheight ) { Error( "GrabPic: Bad size: %i, %i, %i, %i",xl,yl,w,h ); } // crop it to the proper size cropped = (byte *) SafeMalloc( w * h, "Cmd_Grab" ); for ( y = 0 ; y < h ; y++ ) { memcpy( cropped + y * w, byteimage + ( y + yl ) * byteimagewidth + xl, w ); } // save off the new image printf( "saving %s\n", savename ); CreatePath( savename ); WritePCXfile( savename, cropped, w, h, lbmpalette ); free( cropped ); } /* ============== Cmd_Raw $grab filename x y width height ============== */ void Cmd_Raw( void ){ int xl,yl,w,h,y; byte *cropped; char savename[1024]; char dest[1024]; GetScriptToken( false ); sprintf( savename, "%s%s.lmp", gamedir, token ); if ( g_release ) { sprintf( dest, "%s.lmp", token ); ReleaseFile( dest ); return; } GetScriptToken( false ); xl = atoi( token ); GetScriptToken( false ); yl = atoi( token ); GetScriptToken( false ); w = atoi( token ); GetScriptToken( false ); h = atoi( token ); if ( xl < 0 || yl < 0 || w < 0 || h < 0 || xl + w > byteimagewidth || yl + h > byteimageheight ) { Error( "GrabPic: Bad size: %i, %i, %i, %i",xl,yl,w,h ); } // crop it to the proper size cropped = (byte *) SafeMalloc( w * h, "Cmd_Raw" ); for ( y = 0 ; y < h ; y++ ) { memcpy( cropped + y * w, byteimage + ( y + yl ) * byteimagewidth + xl, w ); } // save off the new image printf( "saving %s\n", savename ); CreatePath( savename ); SaveFile( savename, cropped, w * h ); free( cropped ); } /* ============================================================================= COLORMAP GRABBING ============================================================================= */ /* =============== BestColor =============== */ byte BestColor( int r, int g, int b, int start, int stop ){ int i; int dr, dg, db; int bestdistortion, distortion; int bestcolor; byte *pal; // // let any color go to 0 as a last resort // bestdistortion = 256 * 256 * 4; bestcolor = 0; pal = colormap_palette + start * 3; for ( i = start ; i <= stop ; i++ ) { dr = r - (int)pal[0]; dg = g - (int)pal[1]; db = b - (int)pal[2]; pal += 3; distortion = dr * dr + dg * dg + db * db; if ( distortion < bestdistortion ) { if ( !distortion ) { return i; // perfect match } bestdistortion = distortion; bestcolor = i; } } return bestcolor; } /* ============== Cmd_Colormap $colormap filename the brightes colormap is first in the table (FIXME: reverse this now?) 64 rows of 256 : lightmaps 256 rows of 256 : translucency table ============== */ void Cmd_Colormap( void ){ int levels, brights; int l, c; float frac, red, green, blue; float range; byte *cropped, *lump_p; char savename[1024]; char dest[1024]; colormap_issued = true; if ( !g_release ) { memcpy( colormap_palette, lbmpalette, 768 ); } if ( !ScriptTokenAvailable() ) { // just setting colormap_issued return; } GetScriptToken( false ); sprintf( savename, "%spics/%s.pcx", gamedir, token ); if ( g_release ) { sprintf( dest, "pics/%s.pcx", token ); ReleaseFile( dest ); return; } range = 2; levels = 64; brights = 1; // ignore 255 (transparent) cropped = (byte *) SafeMalloc( ( levels + 256 ) * 256, "Cmd_ColorMap" ); lump_p = cropped; // shaded levels for ( l = 0; l < levels; l++ ) { frac = range - range * (float)l / ( levels - 1 ); for ( c = 0 ; c < 256 - brights ; c++ ) { red = lbmpalette[c * 3]; green = lbmpalette[c * 3 + 1]; blue = lbmpalette[c * 3 + 2]; red = (int)( red * frac + 0.5 ); green = (int)( green * frac + 0.5 ); blue = (int)( blue * frac + 0.5 ); // // note: 254 instead of 255 because 255 is the transparent color, and we // don't want anything remapping to that // don't use color 0, because NT can't remap that (or 255) // *lump_p++ = BestColor( red,green,blue, 1, 254 ); } // fullbrights allways stay the same for ( ; c < 256 ; c++ ) *lump_p++ = c; } // 66% transparancy table for ( l = 0; l < 255; l++ ) { for ( c = 0 ; c < 255 ; c++ ) { red = lbmpalette[c * 3] * 0.33 + lbmpalette[l * 3] * 0.66; green = lbmpalette[c * 3 + 1] * 0.33 + lbmpalette[l * 3 + 1] * 0.66; blue = lbmpalette[c * 3 + 2] * 0.33 + lbmpalette[l * 3 + 2] * 0.66; *lump_p++ = BestColor( red,green,blue, 1, 254 ); } *lump_p++ = 255; } for ( c = 0 ; c < 256 ; c++ ) *lump_p++ = 255; // save off the new image printf( "saving %s\n", savename ); CreatePath( savename ); WritePCXfile( savename, cropped, 256, levels + 256, lbmpalette ); free( cropped ); } /* ============================================================================= MIPTEX GRABBING ============================================================================= */ byte pixdata[256]; int d_red, d_green, d_blue; byte palmap[32][32][32]; qboolean palmap_built; /* ============= FindColor ============= */ int FindColor( int r, int g, int b ){ int bestcolor; if ( r > 255 ) { r = 255; } if ( r < 0 ) { r = 0; } if ( g > 255 ) { g = 255; } if ( g < 0 ) { g = 0; } if ( b > 255 ) { b = 255; } if ( b < 0 ) { b = 0; } #ifndef TABLECOLORS bestcolor = BestColor( r, g, b, 0, 254 ); #else bestcolor = palmap[r >> 3][g >> 3][b >> 3]; #endif return bestcolor; } void BuildPalmap( void ){ #ifdef TABLECOLORS int r, g, b; int bestcolor; if ( palmap_built ) { return; } palmap_built = true; for ( r = 4 ; r < 256 ; r += 8 ) { for ( g = 4 ; g < 256 ; g += 8 ) { for ( b = 4 ; b < 256 ; b += 8 ) { bestcolor = BestColor( r, g, b, 1, 254 ); palmap[r >> 3][g >> 3][b >> 3] = bestcolor; } } } #endif if ( !colormap_issued ) { Error( "You must issue a $colormap command first" ); } } /* ============= AveragePixels ============= */ byte AveragePixels( int count ){ int r,g,b; int i; int vis; int pix; int bestcolor; byte *pal; int fullbright; vis = 0; r = g = b = 0; fullbright = 0; for ( i = 0 ; i < count ; i++ ) { pix = pixdata[i]; r += lbmpalette[pix * 3]; g += lbmpalette[pix * 3 + 1]; b += lbmpalette[pix * 3 + 2]; vis++; } r /= vis; g /= vis; b /= vis; // error diffusion r += d_red; g += d_green; b += d_blue; // // find the best color // bestcolor = FindColor( r, g, b ); // error diffusion pal = colormap_palette + bestcolor * 3; d_red = r - (int)pal[0]; d_green = g - (int)pal[1]; d_blue = b - (int)pal[2]; return bestcolor; } typedef enum { pt_contents, pt_flags, pt_animvalue, pt_altnamevalue, pt_damagenamevalue, pt_flagvalue, pt_materialvalue, pt_scale, pt_mip, pt_detail, pt_gl, pt_nomip, pt_detailer, } parmtype_t; typedef struct { char *name; int flags; parmtype_t type; } mipparm_t; mipparm_t mipparms[] = { // utility content attributes {"pushpull",CONTENTS_PUSHPULL, pt_contents}, {"water", CONTENTS_WATER, pt_contents}, {"slime", CONTENTS_SLIME, pt_contents}, // mildly damaging {"lava", CONTENTS_LAVA, pt_contents}, // very damaging {"window", CONTENTS_WINDOW, pt_contents}, // solid, but doesn't eat internal textures {"mist", CONTENTS_MIST, pt_contents}, // non-solid window {"origin", CONTENTS_ORIGIN, pt_contents}, // center of rotating brushes {"playerclip", CONTENTS_PLAYERCLIP, pt_contents}, {"monsterclip", CONTENTS_MONSTERCLIP, pt_contents}, // utility surface attributes {"hint", SURF_HINT, pt_flags}, {"skip", SURF_SKIP, pt_flags}, {"light", SURF_LIGHT, pt_flagvalue}, // value is the light quantity {"animspeed",SURF_ANIMSPEED, pt_flagvalue}, // value will hold the anim speed in fps // texture chaining {"anim", 0, pt_animvalue}, // animname is the next animation {"alt", 0, pt_altnamevalue}, // altname is the alternate texture {"damage", 0, pt_damagenamevalue}, // damagename is the damage texture {"scale", 0, pt_scale}, // next two values are for scale {"mip", 0, pt_mip}, {"detail", 0, pt_detail}, {"GL_ZERO", GL_ZERO, pt_gl}, {"GL_ONE", GL_ONE, pt_gl}, {"GL_SRC_COLOR", GL_SRC_COLOR, pt_gl}, {"GL_ONE_MINUS_SRC_COLOR", GL_ONE_MINUS_SRC_COLOR, pt_gl}, {"GL_DST_COLOR", GL_DST_COLOR, pt_gl}, {"GL_ONE_MINUS_DST_COLOR", GL_ONE_MINUS_DST_COLOR, pt_gl}, {"GL_SRC_ALPHA", GL_SRC_ALPHA, pt_gl}, {"GL_ONE_MINUS_SRC_ALPHA", GL_ONE_MINUS_SRC_ALPHA, pt_gl}, {"GL_DST_ALPHA", GL_DST_ALPHA, pt_gl}, {"GL_ONE_MINUS_DST_ALPHA", GL_ONE_MINUS_DST_ALPHA, pt_gl}, {"GL_SRC_ALPHA_SATURATE", GL_SRC_ALPHA_SATURATE, pt_gl}, // server attributes {"slick", SURF_SLICK, pt_flags}, // drawing attributes {"sky", SURF_SKY, pt_flags}, {"warping", SURF_WARP, pt_flags}, // only valid with 64x64 textures {"trans33", SURF_TRANS33, pt_flags}, // translucent should allso set fullbright {"trans66", SURF_TRANS66, pt_flags}, {"flowing", SURF_FLOWING, pt_flags}, // flow direction towards angle 0 {"nodraw", SURF_NODRAW, pt_flags}, // for clip textures and trigger textures {"alpha", SURF_ALPHA_TEXTURE, pt_flags}, {"undulate", SURF_UNDULATE, pt_flags}, // rock surface up and down... {"skyreflect", SURF_SKYREFLECT, pt_flags}, // liquid will somewhat reflect the sky - not quite finished.... {"material", SURF_MATERIAL, pt_materialvalue}, {"metal", SURF_TYPE_METAL, pt_flags}, {"stone", SURF_TYPE_STONE, pt_flags}, {"wood", SURF_TYPE_WOOD, pt_flags}, {"m_nomip", 0, pt_nomip}, {"m_detail", 0, pt_detailer}, {NULL, 0, pt_contents} }; /* ============== Cmd_Mip $mip filename x y width height must be multiples of sixteen SURF_WINDOW ============== */ void Cmd_Mip( void ){ int xl,yl,xh,yh,w,h; byte *dest, *source; int flags, value, contents; mipparm_t *mp; char lumpname[128]; char altname[128]; char animname[128]; char damagename[128]; byte buffer[MAX_IMAGE_SIZE * MAX_IMAGE_SIZE]; unsigned bufferl[MAX_IMAGE_SIZE * MAX_IMAGE_SIZE]; materialtype_t *mat; char filename[1024]; unsigned *destl, *sourcel; int linedelta, x, y; int size; miptex_t *qtex; miptex32_t *qtex32; float scale_x, scale_y; int mip_scale; // detail texturing char dt_name[128]; float dt_scale_x, dt_scale_y; float dt_u, dt_v; float dt_alpha; int dt_src_blend_mode, dt_dst_blend_mode; int flags2; GetScriptToken( false ); strcpy( lumpname, token ); GetScriptToken( false ); xl = atoi( token ); GetScriptToken( false ); yl = atoi( token ); GetScriptToken( false ); w = atoi( token ); GetScriptToken( false ); h = atoi( token ); total_x += w; total_y += h; total_textures++; if ( ( w & 15 ) || ( h & 15 ) ) { Error( "line %i: miptex sizes must be multiples of 16", scriptline ); } flags = 0; flags2 = 0; contents = 0; value = 0; mip_scale = 0; altname[0] = animname[0] = damagename[0] = 0; scale_x = scale_y = 0.5; // detail texturing dt_name[0] = 0; dt_scale_x = dt_scale_y = 0.0; dt_u = dt_v = 0.0; dt_alpha = 0.0; dt_src_blend_mode = dt_dst_blend_mode = 0; // get optional flags and values while ( ScriptTokenAvailable() ) { GetScriptToken( false ); for ( mp = mipparms ; mp->name ; mp++ ) { if ( !strcmp( mp->name, token ) ) { switch ( mp->type ) { case pt_animvalue: GetScriptToken( false ); // specify the next animation frame strcpy( animname, token ); break; case pt_altnamevalue: GetScriptToken( false ); // specify the alternate texture strcpy( altname, token ); break; case pt_damagenamevalue: GetScriptToken( false ); // specify the damage texture strcpy( damagename, token ); break; case pt_flags: flags |= mp->flags; break; case pt_contents: contents |= mp->flags; break; case pt_flagvalue: flags |= mp->flags; GetScriptToken( false ); // specify the light value value = atoi( token ); break; case pt_materialvalue: GetScriptToken( false ); for ( mat = materialtypes ; mat->name ; mat++ ) { if ( !strcmp( mat->name, token ) ) { // assumes SURF_MATERIAL is in top 8 bits flags = ( flags & 0x0FFFFFF ) | ( mat->value << 24 ); break; } } break; case pt_scale: GetScriptToken( false ); // specify the x scale scale_x = atof( token ); GetScriptToken( false ); // specify the y scale scale_y = atof( token ); break; case pt_mip: mip_scale = 1; break; case pt_detailer: flags2 |= MIP32_DETAILER_FLAG2; break; case pt_nomip: flags2 |= MIP32_NOMIP_FLAG2; break; case pt_detail: GetScriptToken( false ); strcpy( dt_name, token ); GetScriptToken( false ); dt_scale_x = atof( token ); GetScriptToken( false ); dt_scale_y = atof( token ); GetScriptToken( false ); dt_u = atof( token ); GetScriptToken( false ); dt_v = atof( token ); GetScriptToken( false ); dt_alpha = atof( token ); GetScriptToken( false ); for ( mp = mipparms ; mp->name ; mp++ ) { if ( !strcmp( mp->name, token ) ) { if ( mp->type == pt_gl ) { dt_src_blend_mode = mp->flags; break; } } } if ( !mp->name ) { Error( "line %i: invalid gl blend mode %s", scriptline, token ); } GetScriptToken( false ); for ( mp = mipparms ; mp->name ; mp++ ) { if ( !strcmp( mp->name, token ) ) { if ( mp->type == pt_gl ) { dt_dst_blend_mode = mp->flags; break; } } } if ( !mp->name ) { Error( "line %i: invalid gl blend mode %s", scriptline, token ); } break; } break; } } if ( !mp->name ) { Error( "line %i: unknown parm %s", scriptline, token ); } } if ( g_release ) { return; // textures are only released by $maps } xh = xl + w; yh = yl + h; if ( xh * yh > MAX_IMAGE_SIZE * MAX_IMAGE_SIZE ) { Error( "line %i image %s: image is too big!", scriptline, lumpname ); } if ( TrueColorImage ) { if ( xl >= longimagewidth || xh > longimagewidth || yl >= longimageheight || yh > longimageheight ) { Error( "line %i image %s: bad clip dimmensions (%d,%d) (%d,%d) > image (%d,%d)", scriptline, lumpname, xl,yl,w,h,longimagewidth,longimageheight ); } sourcel = longimage + ( yl * longimagewidth ) + xl; destl = bufferl; linedelta = ( longimagewidth - w ); for ( y = yl ; y < yh ; y++ ) { for ( x = xl ; x < xh ; x++ ) { *destl++ = *sourcel++; // RGBA } sourcel += linedelta; } qtex32 = CreateMip32( bufferl, w, h, &size, true ); qtex32->flags |= LittleLong( flags ); qtex32->flags2 |= LittleLong( flags2 ); qtex32->contents = LittleLong( contents ); qtex32->value = LittleLong( value ); qtex32->scale_x = scale_x; qtex32->scale_y = scale_y; qtex32->mip_scale = mip_scale; sprintf( qtex32->name, "%s/%s", mip_prefix, lumpname ); if ( animname[0] ) { sprintf( qtex32->animname, "%s/%s", mip_prefix, animname ); } if ( altname[0] ) { sprintf( qtex32->altname, "%s/%s", mip_prefix, altname ); } if ( damagename[0] ) { sprintf( qtex32->damagename, "%s/%s", mip_prefix, damagename ); } if ( dt_name[0] & ( ( flags2 & MIP32_DETAILER_FLAG2 ) == 0 ) ) { sprintf( qtex32->dt_name, "%s/%s", mip_prefix, dt_name ); qtex32->dt_scale_x = dt_scale_x; qtex32->dt_scale_y = dt_scale_y; qtex32->dt_u = dt_u; qtex32->dt_v = dt_v; qtex32->dt_alpha = dt_alpha; qtex32->dt_src_blend_mode = dt_src_blend_mode; qtex32->dt_dst_blend_mode = dt_dst_blend_mode; } // // write it out // sprintf( filename, "%stextures/%s/%s.m32", g_outputDir, mip_prefix, lumpname ); if ( qtex32->flags & ( SURF_ALPHA_TEXTURE ) ) { printf( "writing %s with ALPHA\n", filename ); } else{ printf( "writing %s\n", filename ); } SaveFile( filename, (byte *)qtex32, size ); free( qtex32 ); } else { if ( xl >= byteimagewidth || xh > byteimagewidth || yl >= byteimageheight || yh > byteimageheight ) { Error( "line %i: bad clip dimmensions (%d,%d) (%d,%d) > image (%d,%d)", scriptline, xl,yl,w,h,byteimagewidth,byteimageheight ); } source = byteimage + yl * byteimagewidth + xl; dest = buffer; linedelta = byteimagewidth - w; for ( y = yl ; y < yh ; y++ ) { for ( x = xl ; x < xh ; x++ ) { *dest++ = *source++; } source += linedelta; } qtex = CreateMip( buffer, w, h, lbmpalette, &size, true ); qtex->flags = LittleLong( flags ); qtex->contents = LittleLong( contents ); qtex->value = LittleLong( value ); sprintf( qtex->name, "%s/%s", mip_prefix, lumpname ); if ( animname[0] ) { sprintf( qtex->animname, "%s/%s", mip_prefix, animname ); } // // write it out // sprintf( filename, "%stextures/%s/%s.m8", g_outputDir, mip_prefix, lumpname ); printf( "writing %s\n", filename ); SaveFile( filename, (byte *)qtex, size ); free( qtex ); } } /* =============== Cmd_Mippal =============== */ void Cmd_Mippal( void ){ colormap_issued = true; if ( g_release ) { return; } memcpy( colormap_palette, lbmpalette, 768 ); BuildPalmap(); } /* =============== Cmd_Mipdir =============== */ void Cmd_Mipdir( void ){ char filename[1024]; GetScriptToken( false ); strcpy( mip_prefix, token ); // create the directory if needed sprintf( filename, "%stextures", g_outputDir ); Q_mkdir( filename ); sprintf( filename, "%stextures/%s", g_outputDir, mip_prefix ); Q_mkdir( filename ); } /* ============================================================================= ENVIRONMENT MAP GRABBING Creates six pcx files from tga files without any palette edge seams also copies the tga files for GL rendering. ============================================================================= */ // 3dstudio environment map suffixes char *suf[6] = {"rt", "ft", "lf", "bk", "up", "dn"}; /* ================= Cmd_Environment ================= */ void Cmd_Environment( void ){ char name[1024]; int i, x, y; byte image[256 * 256]; byte *tga; GetScriptToken( false ); if ( g_release ) { for ( i = 0 ; i < 6 ; i++ ) { sprintf( name, "env/%s%s.pcx", token, suf[i] ); ReleaseFile( name ); sprintf( name, "env/%s%s.tga", token, suf[i] ); ReleaseFile( name ); } return; } // get the palette BuildPalmap(); sprintf( name, "%senv/", gamedir ); CreatePath( name ); // convert the images for ( i = 0 ; i < 6 ; i++ ) { sprintf( name, "%senv/%s%s.tga", gamedir, token, suf[i] ); printf( "loading %s...\n", name ); LoadTGA( name, &tga, NULL, NULL ); for ( y = 0 ; y < 256 ; y++ ) { for ( x = 0 ; x < 256 ; x++ ) { image[y * 256 + x] = FindColor( tga[( y * 256 + x ) * 4 + 0],tga[( y * 256 + x ) * 4 + 1],tga[( y * 256 + x ) * 4 + 2] ); } } free( tga ); sprintf( name, "%senv/%s%s.pcx", gamedir, token, suf[i] ); if ( FileTime( name ) != -1 ) { printf( "%s already exists, not overwriting.\n", name ); } else{ WritePCXfile( name, image, 256, 256, colormap_palette ); } } }