gtkradiant/tools/quake2/qdata_heretic2/tmix.c

699 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