mirror of
https://github.com/UberGames/GtkRadiant.git
synced 2024-11-25 21:31:12 +00:00
667 lines
14 KiB
C
667 lines
14 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 "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
|