/* 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> 16; palette_g[i] = (palette[i] & 0x0000ff00) >> 8; palette_b[i] = (palette[i] & 0x000000ff); } for(i=0;i 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>16; frac += fracstep; } frac = 3*(fracstep>>2); for (i=0 ; i>16; frac += fracstep; } cached = 0; for (i=0 ; ir + (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>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; } } } void GL_MipMap (byte *out, byte *in, int width, int height) { int i, j; width <<=3; height <<= 1; 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; } } } 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 ; ybyteimagewidth || 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 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 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 ; yflags |= 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 ; yflags = 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); } }