gtkradiant/tools/quake2/qdata/images.c

768 lines
16 KiB
C

/*
Copyright (C) 1999-2007 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"
#include "inout.h"
char mip_prefix[1024]; // directory to dump the textures in
qboolean colormap_issued;
byte colormap_palette[768];
/*
==============
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;
}
}
/*
==============
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];
GetToken( 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;
}
GetToken( false );
xl = atoi( token );
GetToken( false );
yl = atoi( token );
GetToken( false );
w = atoi( token );
GetToken( 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 = malloc( w * h );
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];
GetToken( false );
sprintf( savename, "%s%s.lmp", gamedir, token );
if ( g_release ) {
sprintf( dest, "%s.lmp", token );
ReleaseFile( dest );
return;
}
GetToken( false );
xl = atoi( token );
GetToken( false );
yl = atoi( token );
GetToken( false );
w = atoi( token );
GetToken( 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 = malloc( w * h );
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 ( !TokenAvailable() ) { // just setting colormap_issued
return;
}
GetToken( 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 = malloc( ( levels + 256 ) * 256 );
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_flagvalue
} parmtype_t;
typedef struct
{
char *name;
int flags;
parmtype_t type;
} mipparm_t;
mipparm_t mipparms[] =
{
// utility content attributes
{"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
// texture chaining
{"anim", 0, pt_animvalue}, // value is the next animation
// 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
{NULL, 0, pt_contents}
};
/*
==============
Cmd_Mip
$mip filename x y width height <OPTIONS>
must be multiples of sixteen
SURF_WINDOW
==============
*/
void Cmd_Mip( void ){
int x,y,xl,yl,xh,yh,w,h;
byte *screen_p, *source;
int linedelta;
miptex_t *qtex;
int miplevel, mipstep;
int xx, yy, pix;
int count;
int flags, value, contents;
mipparm_t *mp;
char lumpname[64];
byte *lump_p;
char filename[1024];
char animname[64];
GetToken( false );
strcpy( lumpname, token );
GetToken( false );
xl = atoi( token );
GetToken( false );
yl = atoi( token );
GetToken( false );
w = atoi( token );
GetToken( false );
h = atoi( token );
if ( ( w & 15 ) || ( h & 15 ) ) {
Error( "line %i: miptex sizes must be multiples of 16", scriptline );
}
flags = 0;
contents = 0;
value = 0;
animname[0] = 0;
// get optional flags and values
while ( TokenAvailable() )
{
GetToken( false );
for ( mp = mipparms ; mp->name ; mp++ )
{
if ( !strcmp( mp->name, token ) ) {
switch ( mp->type )
{
case pt_animvalue:
GetToken( false ); // specify the next animation frame
strcpy( animname, token );
break;
case pt_flags:
flags |= mp->flags;
break;
case pt_contents:
contents |= mp->flags;
break;
case pt_flagvalue:
flags |= mp->flags;
GetToken( false ); // specify the light value
value = atoi( token );
break;
}
break;
}
}
if ( !mp->name ) {
Error( "line %i: unknown parm %s", scriptline, token );
}
}
sprintf( filename, "%stextures/%s/%s.wal", gamedir, mip_prefix, lumpname );
if ( g_release ) {
return; // textures are only released by $maps
}
xh = xl + w;
yh = yl + h;
qtex = malloc( sizeof( miptex_t ) + w * h * 2 );
memset( qtex, 0, sizeof( miptex_t ) );
qtex->width = LittleLong( w );
qtex->height = LittleLong( h );
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 );
}
lump_p = (byte *)( &qtex->value + 1 );
screen_p = byteimage + yl * byteimagewidth + xl;
linedelta = byteimagewidth - w;
source = lump_p;
qtex->offsets[0] = LittleLong( lump_p - (byte *)qtex );
for ( y = yl ; y < yh ; y++ )
{
for ( x = xl ; x < xh ; x++ )
{
pix = *screen_p++;
if ( pix == 255 ) {
pix = 1; // should never happen
}
*lump_p++ = pix;
}
screen_p += linedelta;
}
//
// subsample for greater mip levels
//
d_red = d_green = d_blue = 0; // no distortion yet
for ( miplevel = 1 ; miplevel < 4 ; miplevel++ )
{
qtex->offsets[miplevel] = LittleLong( lump_p - (byte *)qtex );
mipstep = 1 << miplevel;
for ( y = 0 ; y < h ; y += mipstep )
{
for ( x = 0 ; x < w ; x += mipstep )
{
count = 0;
for ( yy = 0 ; yy < mipstep ; yy++ )
for ( xx = 0 ; xx < mipstep ; xx++ )
{
pixdata[count] = source[ ( y + yy ) * w + x + xx ];
count++;
}
*lump_p++ = AveragePixels( count );
}
}
}
//
// dword align the size
//
while ( (int)lump_p & 3 )
*lump_p++ = 0;
//
// write it out
//
printf( "writing %s\n", filename );
SaveFile( filename, (byte *)qtex, lump_p - (byte *)qtex );
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];
GetToken( false );
strcpy( mip_prefix, token );
// create the directory if needed
sprintf( filename, "%stextures", gamedir, mip_prefix );
Q_mkdir( filename );
sprintf( filename, "%stextures/%s", gamedir, 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;
GetToken( 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 );
}
}
}