/* 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" #include "flex.h" #define MAXFILES 2048 typedef struct { int x; int y; int w; int h; int cw; int ch; int rw; int index; int depth; int col; int baseline; char name[128]; } Coords; int filenum; int valid; Coords in[MAXFILES]; Coords out; char outscript[256]; char sourcedir[256]; char outusage[256]; char root[32]; int destsize = 0; byte *pixels = NULL; // Buffer to load image long *outpixels = NULL; // Buffer to store combined textures long *usagemap = NULL; // Buffer of usage map void *bmptemp = NULL; // Buffer of usage map byte *map = NULL; int xcharsize; int ycharsize; int dosort = 0; int missed = 0; int overlap = 0; int nobaseline = 0; int percent; ////////////////////////////////////////////////// // Setting the char based usage map // ////////////////////////////////////////////////// byte TryPlace( Coords *coord ){ int x, y; byte entry = 0; byte *mapitem; mapitem = map + ( coord->x / xcharsize ) + ( ( coord->y / ycharsize ) * out.cw ); for ( y = 0; y < coord->ch; y++, mapitem += out.cw - coord->cw ) { for ( x = 0; x < coord->cw; x++ ) { if ( entry |= *mapitem++ & 8 ) { return( entry ); } } } return( entry ); } void SetMap( Coords *coord ){ int x, y; byte *mapitem; mapitem = map + ( coord->x / xcharsize ) + ( ( coord->y / ycharsize ) * out.cw ); for ( y = 0; y < coord->ch; y++, mapitem += out.cw - coord->cw ) for ( x = 0; x < coord->cw; x++ ) *mapitem++ |= 8; } ////////////////////////////////////////////////// // Setting the pixel based usage map // ////////////////////////////////////////////////// void CheckOverlap( Coords *coord ){ int x; int y; long *dest; x = coord->x; y = coord->y; dest = (long *)( usagemap + x + ( y * out.w ) ); for ( y = 0; y < coord->h; y++, dest += out.w - coord->w ) { for ( x = 0; x < coord->w; x++ ) { if ( *dest++ ) { overlap++; return; } } } } void SetUsageMap( Coords *coord ){ int x; int y; long *dest; x = coord->x; y = coord->y; dest = (long *)( usagemap + x + ( y * out.w ) ); for ( y = 0; y < coord->h; y++, dest += out.w - coord->w ) { for ( x = 0; x < coord->w; x++ ) { *dest++ = coord->col; } } } ////////////////////////////////////////////////// // Flips the BMP image to the correct way up // ////////////////////////////////////////////////// void CopyLine( byte *dest, byte *src, int size ){ int x; for ( x = 0; x < size; x++ ) *dest++ = *src++; } /****************************************************/ /* Printing headers etc */ /****************************************************/ void RemoveLeading( char *name ){ int i; char temp[128]; for ( i = strlen( name ) - 1; i > 0; i-- ) { if ( ( name[i] == '\\' ) || ( name[i] == '/' ) ) { strcpy( temp, name + i + 1 ); strcpy( name, temp ); return; } } } void RemoveExt( char *name ){ while ( ( *name != '.' ) && *name ) name++; *name = 0; } /****************************************************/ /* Misc calcualtions */ /****************************************************/ int TotalArea(){ int i; int total = 0; for ( i = 0; i < ( filenum + 2 ); i++ ) total += in[i].w * in[i].h; return( total ); } /****************************************************/ /* Setup and checking of all info */ /****************************************************/ void InitVars(){ filenum = 0; valid = 0; dosort = 0; missed = 0; overlap = 0; nobaseline = 0; memset( outscript, 0, sizeof( outscript ) ); memset( outscript, 0, sizeof( sourcedir ) ); memset( outscript, 0, sizeof( outusage ) ); memset( outscript, 0, sizeof( root ) ); memset( in, 0, sizeof( in ) ); memset( &out, 0, sizeof( out ) ); } void Cleanup(){ if ( pixels ) { free( pixels ); } if ( usagemap ) { free( usagemap ); } if ( outpixels ) { free( outpixels ); } if ( bmptemp ) { free( bmptemp ); } if ( map ) { free( map ); } } typedef struct glxy_s { float xl, yt, xr, yb; int w, h, baseline; } glxy_t; int SaveScript( char *name ){ FILE *fp; int i, j; glxy_t buff; if ( fp = fopen( name, "wb" ) ) { for ( j = 0; j < filenum; j++ ) { for ( i = 0; i < filenum; i++ ) { if ( in[i].index == j ) { if ( in[i].depth ) { buff.xl = (float)in[i].x / (float)out.w; buff.yt = (float)in[i].y / (float)out.h; buff.xr = ( (float)in[i].w + (float)in[i].x ) / (float)out.w; buff.yb = ( (float)in[i].h + (float)in[i].y ) / (float)out.h; buff.w = in[i].w; buff.h = in[i].h; buff.baseline = in[i].baseline; } else { memset( &buff, 0, sizeof( glxy_t ) ); } fwrite( &buff, 1, sizeof( glxy_t ), fp ); i = filenum; } } } fclose( fp ); return( true ); } else{ return( false ); } } int GetScriptInfo( char *name ){ FILE *fp; char buffer[256]; char tempbuff[256]; char delims[] = {" \t,\n"}; printf( "Opening script file %s.\n", name ); if ( fp = fopen( name, "r" ) ) { while ( fgets( buffer, 256, fp ) ) { if ( strncmp( buffer, "//", 2 ) && strncmp( buffer, "\n", 1 ) ) { strupr( buffer ); strcpy( tempbuff, buffer ); if ( strcmp( strtok( tempbuff, delims ), "OUTPUT" ) == 0 ) { strcpy( out.name, strtok( NULL, delims ) ); strlwr( out.name ); } strcpy( tempbuff, buffer ); if ( strcmp( strtok( tempbuff, delims ), "SOURCEDIR" ) == 0 ) { strcpy( tempbuff, strtok( NULL, delims ) ); strcpy( sourcedir, ExpandPathAndArchive( tempbuff ) ); } strcpy( tempbuff, buffer ); if ( strcmp( strtok( tempbuff, delims ), "DOSORT" ) == 0 ) { dosort = 1; } strcpy( tempbuff, buffer ); if ( strcmp( strtok( tempbuff, delims ), "XCHARSIZE" ) == 0 ) { xcharsize = strtol( strtok( NULL, delims ), NULL, 0 ); } strcpy( tempbuff, buffer ); if ( strcmp( strtok( tempbuff, delims ), "YCHARSIZE" ) == 0 ) { ycharsize = strtol( strtok( NULL, delims ), NULL, 0 ); } strcpy( tempbuff, buffer ); if ( strcmp( strtok( tempbuff, delims ), "OUTSCRIPT" ) == 0 ) { strcpy( outscript, strtok( NULL, delims ) ); strlwr( outscript ); } strcpy( tempbuff, buffer ); if ( strcmp( strtok( tempbuff, delims ), "OUTUSAGE" ) == 0 ) { strcpy( outusage, strtok( NULL, delims ) ); } strcpy( tempbuff, buffer ); if ( strcmp( strtok( tempbuff, delims ), "POS" ) == 0 ) { out.w = strtol( strtok( NULL, delims ), NULL, 0 ); out.h = strtol( strtok( NULL, delims ), NULL, 0 ); } strcpy( tempbuff, buffer ); if ( strcmp( strtok( tempbuff, delims ), "FILE" ) == 0 ) { strcpy( in[filenum].name, strtok( NULL, delims ) ); in[filenum].x = strtol( strtok( NULL, delims ), NULL, 0 ); in[filenum].y = strtol( strtok( NULL, delims ), NULL, 0 ); in[filenum].col = strtol( strtok( NULL, delims ), NULL, 0 ); filenum++; } } } fclose( fp ); return( true ); } else { printf( "ERROR : Could not open script file.\n" ); return( false ); } } int CheckVars(){ int i; if ( out.name[0] == 0 ) { printf( "ERROR : No output name specified.\n" ); return( false ); } if ( ( out.w <= 0 ) || ( out.h <= 0 ) ) { printf( "ERROR : Invalid VRAM coordinates.\n" ); return( false ); } if ( filenum == 0 ) { printf( "ERROR : No input files specified.\n" ); return( false ); } for ( i = 0; i < filenum; i++ ) if ( in[i].name[0] == 0 ) { printf( "ERROR : Input filename invalid.\n" ); return( false ); } return( true ); } // Makes sure texture is totally within the output area int CheckCoords( Coords *coord ){ if ( ( coord->x + coord->w ) > out.w ) { return( false ); } if ( ( coord->y + coord->h ) > out.h ) { return( false ); } return( true ); } // Gets the width, height, palette width and palette height of each BMP file int GetFileDimensions(){ int i; int width, height; char name[128]; for ( i = 0; i < filenum; i++ ) { in[i].index = i; strcpy( name, sourcedir ); strcat( name, in[i].name ); printf( "Getting file dimensions, file : %s \r", in[i].name ); if ( FileExists( name ) ) { LoadAnyImage( name, NULL, NULL, &width, &height ); in[i].depth = 32; in[i].rw = width; in[i].w = width; // makes it width in in[i].h = height; in[i].cw = ( in[i].w + ( xcharsize - 1 ) ) / xcharsize; in[i].ch = ( in[i].h + ( ycharsize - 1 ) ) / ycharsize; if ( !CheckCoords( &in[i] ) && ( in[i].x >= 0 ) ) { printf( "Error : texture %s out of bounds.\n", in[i].name ); return( false ); } valid++; } else { in[i].depth = 0; in[i].x = -1; in[i].y = -1; in[i].w = 0; in[i].h = 0; } } printf( "\n\n" ); return( true ); } // Sorts files into order for optimal space finding // Fixed position ones first, followed by the others in descending size // The theory being that it is easier to find space for smaller textures. // size = (width + height) // For space finding it is easier to place a 32x32 than a 128x2 #define WEIGHT 0x8000 void Swap( Coords *a, Coords *b ){ Coords c; c = *a; *a = *b; *b = c; } void SortInNames(){ int i, j; int largest, largcount; int size; printf( "Sorting filenames by size.\n\n" ); for ( j = 0; j < filenum; j++ ) { largest = -1; largcount = -1; for ( i = j; i < filenum; i++ ) { if ( in[i].depth ) { size = in[i].w + in[i].h; if ( ( in[i].x < 0 ) && ( size > largest ) ) { largcount = i; largest = size; } } } if ( ( largcount >= 0 ) && ( largcount != j ) ) { Swap( &in[j], &in[largcount] ); } } } int SetVars( char *name ){ if ( !GetScriptInfo( name ) ) { return( false ); } if ( !CheckVars() ) { return( false ); } destsize = out.w * out.h; out.cw = out.w / xcharsize; out.ch = out.h / ycharsize; if ( ( usagemap = (long *)SafeMalloc( destsize * 4, "" ) ) == NULL ) { return( false ); } if ( ( outpixels = (long *)SafeMalloc( destsize * 4, "" ) ) == NULL ) { return( false ); } if ( ( bmptemp = (void *)SafeMalloc( destsize * 4, "" ) ) == NULL ) { return( false ); } if ( ( map = (byte *)SafeMalloc( destsize / ( xcharsize * ycharsize ), "" ) ) == NULL ) { return( false ); } if ( GetFileDimensions() == false ) { return( false ); } if ( dosort ) { SortInNames(); } return( true ); } /****************************************************/ /* Actual copying routines */ /****************************************************/ int FindCoords( Coords *coord ){ int tx, ty; if ( coord->x >= 0 ) { SetMap( coord ); return( true ); } else { for ( ty = 0; ty < out.ch; ty++ ) { for ( tx = 0; tx < out.cw; tx++ ) { coord->x = ( tx * xcharsize ); coord->y = ( ty * ycharsize ); if ( CheckCoords( coord ) && !TryPlace( coord ) ) { SetMap( coord ); return( true ); } } } } coord->x = -1; coord->y = -1; return( false ); } void CheckBaseline( int i ){ int y; long *pix; in[i].baseline = -1; pix = (long *)pixels; for ( y = 0; y < in[i].h; y++, pix += in[i].w ) { if ( ( *pix & 0x00ffffff ) == 0x00ff00ff ) { in[i].baseline = y; break; } } pix = (long *)pixels; for ( y = 0; y < in[i].w * in[i].h; y++, pix++ ) { if ( ( *pix & 0x00ffffff ) == 0x00ff00ff ) { *pix = 0; } } if ( in[i].baseline == -1 ) { printf( "\nERROR : %s has no baseline\n", in[i].name ); nobaseline++; } } void CopyToMain32( Coords *coord ){ int x; int y; long *source; long *dest; x = coord->x; y = coord->y; source = (long *)pixels; dest = (long *)( outpixels + x + ( y * out.w ) ); for ( y = 0; y < coord->h; y++, dest += out.w - coord->w ) { for ( x = 0; x < coord->w; x++ ) { *dest++ = *source++; } } } void CreateMain(){ int i, count; int width, height; char name[128]; for ( i = 0, count = 0; i < filenum; i++ ) { if ( in[i].depth ) { printf( "\rProcessing %d of %d (%d missed, %d overlapping, %d nobase)\r", count + 1, valid, missed, overlap, nobaseline ); count++; if ( !FindCoords( &in[i] ) ) { missed++; } else { strcpy( name, sourcedir ); strcat( name, in[i].name ); LoadAnyImage( name, &pixels, NULL, &width, &height ); CheckBaseline( i ); CheckOverlap( &in[i] ); CopyToMain32( &in[i] ); SetUsageMap( &in[i] ); } } } } void Cmd_TextureMix(){ miptex32_t *qtex32; char filename[1024]; int size; InitVars(); GetScriptToken( false ); strcpy( root, token ); RemoveExt( root ); RemoveLeading( root ); strcpy( filename, ExpandPathAndArchive( token ) ); if ( SetVars( filename ) ) { // Create combined texture percent = ( ( TotalArea() * 100 ) / ( out.w * out.h ) ); printf( "Total area consumed : %d%%\n", percent ); printf( "Texture resolution : %dx%d pixels.\n", xcharsize, ycharsize ); CreateMain(); // Save image as m32 sprintf( filename, "%spics/misc/%s.m32", gamedir, out.name ); qtex32 = CreateMip32( (unsigned *)outpixels, out.w, out.h, &size, false ); qtex32->contents = 0; qtex32->value = 0; qtex32->scale_x = 1.0; qtex32->scale_y = 1.0; sprintf( qtex32->name, "misc/%s", out.name ); printf( "\n\nwriting %s\n", filename ); SaveFile( filename, (byte *)qtex32, size ); free( qtex32 ); // Save out script file sprintf( filename, "%spics/misc/%s.fnt", gamedir, outscript ); printf( "Writing %s as script file\n", filename ); if ( !SaveScript( filename ) ) { printf( "Unable to save output script.\n" ); } } printf( "Everythings groovy.\n" ); Cleanup(); } // end