halflife-thewastes-sdk/utils/qcsg/textures.c
2001-11-08 00:00:00 +00:00

520 lines
12 KiB
C

/***
*
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
****/
#include "csg.h"
typedef struct
{
char identification[4]; // should be WAD2/WAD3
int numlumps;
int infotableofs;
} wadinfo_t;
typedef struct
{
int filepos;
int disksize;
int size; // uncompressed
char type;
char compression;
char pad1, pad2;
char name[16]; // must be null terminated
int iTexFile; // index of the wad this texture is located in
} lumpinfo_t;
int nummiptex;
lumpinfo_t miptex[MAX_MAP_TEXTURES];
int nTexLumps = 0;
lumpinfo_t *lumpinfo = NULL;
int nTexFiles = 0;
FILE *texfiles[128];
int nWadInclude;
char *pszWadInclude[128];
qboolean wadInclude[128]; // include the textures from this WAD in the BSP
void CleanupName (char *in, char *out)
{
int i;
for (i=0 ; i< 16 ; i++ )
{
if (!in[i])
break;
out[i] = toupper(in[i]);
}
for ( ; i< 16 ; i++ )
out[i] = 0;
}
/*
=================
lump_sorters
=================
*/
int
lump_sorter_by_wad_and_name( const void *lump1, const void *lump2 )
{
lumpinfo_t *plump1 = (lumpinfo_t *)lump1;
lumpinfo_t *plump2 = (lumpinfo_t *)lump2;
if ( plump1->iTexFile == plump2->iTexFile )
return strcmp( plump1->name, plump2->name );
else
return plump1->iTexFile - plump2->iTexFile;
}
int
lump_sorter_by_name( const void *lump1, const void *lump2 )
{
lumpinfo_t *plump1 = (lumpinfo_t *)lump1;
lumpinfo_t *plump2 = (lumpinfo_t *)lump2;
return strcmp( plump1->name, plump2->name );
}
/*
=================
TEX_InitFromWad
=================
*/
qboolean TEX_InitFromWad (char *path)
{
int i;
wadinfo_t wadinfo;
char szTmpPath[512];
char *pszWadFile;
strcpy(szTmpPath, path);
// temporary kludge so we don't have to deal with no occurances of a semicolon
// in the path name ..
if(strchr(szTmpPath, ';') == NULL)
strcat(szTmpPath, ";");
pszWadFile = strtok(szTmpPath, ";");
while(pszWadFile)
{
FILE *texfile; // temporary used in this loop
texfiles[nTexFiles] = fopen(pszWadFile, "rb");
if (!texfiles[nTexFiles])
{
// maybe this wad file has a hard code drive
if (pszWadFile[1] == ':')
{
pszWadFile += 2; // skip past the file
texfiles[nTexFiles] = fopen (pszWadFile, "rb");
}
}
if (!texfiles[nTexFiles])
{
printf ("WARNING: couldn't open %s\n", pszWadFile);
return false;
}
++nTexFiles;
// look and see if we're supposed to include the textures from this WAD in the bsp.
for (i = 0; i < nWadInclude; i++)
{
if (stricmp( pszWadInclude[i], pszWadFile ) == 0)
{
wadInclude[nTexFiles-1] = true;
}
}
// temp assignment to make things cleaner:
texfile = texfiles[nTexFiles-1];
printf ("Using WAD File: %s\n", pszWadFile);
SafeRead(texfile, &wadinfo, sizeof(wadinfo));
if (strncmp (wadinfo.identification, "WAD2", 4) &&
strncmp (wadinfo.identification, "WAD3", 4))
Error ("TEX_InitFromWad: %s isn't a wadfile",pszWadFile);
wadinfo.numlumps = LittleLong(wadinfo.numlumps);
wadinfo.infotableofs = LittleLong(wadinfo.infotableofs);
fseek (texfile, wadinfo.infotableofs, SEEK_SET);
lumpinfo = realloc(lumpinfo, (nTexLumps + wadinfo.numlumps)
* sizeof(lumpinfo_t));
for(i = 0; i < wadinfo.numlumps; i++)
{
SafeRead(texfile, &lumpinfo[nTexLumps], sizeof(lumpinfo_t) -
sizeof(int)); // iTexFile is NOT read from file
CleanupName (lumpinfo[nTexLumps].name, lumpinfo[nTexLumps].name);
lumpinfo[nTexLumps].filepos = LittleLong(lumpinfo[nTexLumps].filepos);
lumpinfo[nTexLumps].disksize = LittleLong(lumpinfo[nTexLumps].disksize);
lumpinfo[nTexLumps].iTexFile = nTexFiles-1;
++nTexLumps;
}
// next wad file
pszWadFile = strtok(NULL, ";");
}
qsort( (void *)lumpinfo, (size_t)nTexLumps, sizeof(lumpinfo[0]), lump_sorter_by_name );
return true;
}
/*
==================
FindTexture
==================
*/
lumpinfo_t *
FindTexture (lumpinfo_t *source )
{
lumpinfo_t *found = NULL;
if ( !( found = bsearch( source, (void *)lumpinfo, (size_t)nTexLumps, sizeof(lumpinfo[0]), lump_sorter_by_name ) ) )
printf ("WARNING: texture %s not found in BSP's wad file list!\n", source->name);
return found;
}
/*
==================
LoadLump
==================
*/
int LoadLump (lumpinfo_t *source, byte *dest, int *texsize)
{
*texsize = 0;
if ( source->filepos )
{
fseek (texfiles[source->iTexFile], source->filepos, SEEK_SET);
*texsize = source->disksize;
// Should we just load the texture header w/o the palette & bitmap?
if ( wadtextures && !wadInclude[source->iTexFile] )
{
// Just read the miptex header and zero out the data offsets.
// We will load the entire texture from the WAD at engine runtime
int i;
miptex_t *miptex = (miptex_t *)dest;
SafeRead (texfiles[source->iTexFile], dest, sizeof(miptex_t) );
for( i=0; i<MIPLEVELS; i++ )
miptex->offsets[i] = 0;
return sizeof(miptex_t);
}
else
{
// Load the entire texture here so the BSP contains the texture
SafeRead (texfiles[source->iTexFile], dest, source->disksize );
return source->disksize;
}
}
printf ("WARNING: texture %s not found in BSP's wad file list!\n", source->name);
return 0;
}
/*
==================
AddAnimatingTextures
==================
*/
void AddAnimatingTextures (void)
{
int base;
int i, j, k;
char name[32];
base = nummiptex;
for (i=0 ; i<base ; i++)
{
if (miptex[i].name[0] != '+' && miptex[i].name[0] != '-')
continue;
strcpy (name, miptex[i].name);
for (j=0 ; j<20 ; j++)
{
if (j < 10)
name[1] = '0'+j;
else
name[1] = 'A'+j-10; // alternate animation
// see if this name exists in the wadfile
for (k=0 ; k < nTexLumps; k++)
if (!strcmp(name, lumpinfo[k].name))
{
FindMiptex (name); // add to the miptex list
break;
}
}
}
if ( nummiptex - base )
printf ("added %i additional animating textures.\n", nummiptex - base);
}
/*
==================
WriteMiptex
==================
*/
void WriteMiptex(void)
{
int i, len, texsize, totaltexsize = 0;
byte *data;
dmiptexlump_t *l;
char *path;
char fullpath[1024];
int start;
texdatasize = 0;
path = ValueForKey (&entities[0], "_wad");
if (!path || !path[0])
{
path = ValueForKey (&entities[0], "wad");
if (!path || !path[0])
{
printf ("WARNING: no wadfile specified\n");
return;
}
}
strcpy(fullpath, path);
start = GetTickCount();
{
if (!TEX_InitFromWad (fullpath))
return;
AddAnimatingTextures ();
}
qprintf( "TEX_InitFromWad & AddAnimatingTextures elapsed time = %ldms\n", GetTickCount()-start );
start = GetTickCount();
{
for (i=0; i<nummiptex; i++ )
{
lumpinfo_t *found;
if ( found = FindTexture( miptex + i ) )
miptex[i] = *found;
else
{
miptex[i].iTexFile = miptex[i].filepos = miptex[i].disksize = 0;
}
}
}
qprintf( "FindTextures elapsed time = %ldms\n", GetTickCount()-start );
start = GetTickCount();
{
int final_miptex, i;
texinfo_t *tx = texinfo;
// Sort them FIRST by wadfile and THEN by name for most efficient loading in the engine.
qsort( (void *)miptex, (size_t)nummiptex, sizeof(miptex[0]), lump_sorter_by_wad_and_name );
// Sleazy Hack 104 Pt 2 - After sorting the miptex array, reset the texinfos to point to the right miptexs
for(i=0; i<numtexinfo; i++, tx++)
{
char *miptex_name = (char *)tx->miptex;
tx->miptex = FindMiptex( miptex_name );
// Free up the originally strdup()'ed miptex_name
free( miptex_name );
}
}
qprintf( "qsort(miptex) elapsed time = %ldms\n", GetTickCount()-start );
start = GetTickCount();
{
// Now setup to get the miptex data (or just the headers if using -wadtextures) from the wadfile
l = (dmiptexlump_t *)dtexdata;
data = (byte *)&l->dataofs[nummiptex];
l->nummiptex = nummiptex;
for (i=0 ; i<nummiptex ; i++)
{
l->dataofs[i] = data - (byte *)l;
len = LoadLump (miptex+i, data, &texsize);
if (data + len - dtexdata >= MAX_MAP_MIPTEX)
Error ("Textures exceeded MAX_MAP_MIPTEX");
if (!len)
l->dataofs[i] = -1; // didn't find the texture
else
{
totaltexsize += texsize;
if ( totaltexsize > MAX_MAP_MIPTEX )
Error("Textures exceeded MAX_MAP_MIPTEX" );
}
data += len;
}
texdatasize = data - dtexdata;
}
qprintf( "LoadLump() elapsed time = %ldms\n", GetTickCount()-start );
}
//==========================================================================
int FindMiptex (char *name)
{
int i;
ThreadLock ();
for (i=0 ; i<nummiptex ; i++)
if (!Q_strcasecmp (name, miptex[i].name))
{
ThreadUnlock ();
return i;
}
if (nummiptex == MAX_MAP_TEXTURES)
Error ("Exceeded MAX_MAP_TEXTURES");
strcpy (miptex[i].name, name);
nummiptex++;
ThreadUnlock ();
return i;
}
int TexinfoForBrushTexture (plane_t *plane, brush_texture_t *bt, vec3_t origin)
{
vec3_t vecs[2];
int sv, tv;
vec_t ang, sinv, cosv;
vec_t ns, nt;
texinfo_t tx, *tc;
int i, j, k;
memset (&tx, 0, sizeof(tx));
tx.miptex = FindMiptex (bt->name);
// Note: FindMiptex() still needs to be called here to add it to the global miptex array
// Very Sleazy Hack 104 - since the tx.miptex index will be bogus after we sort the miptex array later
// Put the string name of the miptex in this "index" until after we are done sorting it in WriteMiptex().
tx.miptex = (int)strdup(bt->name);
// set the special flag
if (bt->name[0] == '*'
|| !Q_strncasecmp (bt->name, "sky",3)
|| !Q_strncasecmp (bt->name, "clip",4)
|| !Q_strncasecmp (bt->name, "origin",6)
|| !Q_strncasecmp (bt->name, "aaatrigger",10))
tx.flags |= TEX_SPECIAL;
if (g_nMapFileVersion < 220)
{
TextureAxisFromPlane(plane, vecs[0], vecs[1]);
}
if (!bt->scale[0])
bt->scale[0] = 1;
if (!bt->scale[1])
bt->scale[1] = 1;
if (g_nMapFileVersion < 220)
{
// rotate axis
if (bt->rotate == 0)
{ sinv = 0 ; cosv = 1; }
else if (bt->rotate == 90)
{ sinv = 1 ; cosv = 0; }
else if (bt->rotate == 180)
{ sinv = 0 ; cosv = -1; }
else if (bt->rotate == 270)
{ sinv = -1 ; cosv = 0; }
else
{
ang = bt->rotate / 180 * Q_PI;
sinv = sin(ang);
cosv = cos(ang);
}
if (vecs[0][0])
sv = 0;
else if (vecs[0][1])
sv = 1;
else
sv = 2;
if (vecs[1][0])
tv = 0;
else if (vecs[1][1])
tv = 1;
else
tv = 2;
for (i=0 ; i<2 ; i++)
{
ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
vecs[i][sv] = ns;
vecs[i][tv] = nt;
}
for (i=0 ; i<2 ; i++)
for (j=0 ; j<3 ; j++)
tx.vecs[i][j] = vecs[i][j] / bt->scale[i];
}
else
{
tx.vecs[0][0] = bt->UAxis[0] / bt->scale[0];
tx.vecs[0][1] = bt->UAxis[1] / bt->scale[0];
tx.vecs[0][2] = bt->UAxis[2] / bt->scale[0];
tx.vecs[1][0] = bt->VAxis[0] / bt->scale[1];
tx.vecs[1][1] = bt->VAxis[1] / bt->scale[1];
tx.vecs[1][2] = bt->VAxis[2] / bt->scale[1];
}
tx.vecs[0][3] = bt->shift[0] + DotProduct( origin, tx.vecs[0] );
tx.vecs[1][3] = bt->shift[1] + DotProduct( origin, tx.vecs[1] );
//
// find the texinfo
//
ThreadLock ();
tc = texinfo;
for (i=0 ; i<numtexinfo ; i++, tc++)
{
// Sleazy hack 104, Pt 3 - Use strcmp on names to avoid dups
if ( strcmp( (char *)(tc->miptex), (char *)(tx.miptex)) != 0 )
continue;
if (tc->flags != tx.flags)
continue;
for (j=0 ; j<2 ; j++)
{
for (k=0 ; k<4 ; k++)
{
if (tc->vecs[j][k] != tx.vecs[j][k])
goto skip;
}
}
ThreadUnlock ();
return i;
skip:;
}
if (numtexinfo == MAX_MAP_TEXINFO)
Error ("Exceeded MAX_MAP_TEXINFO");
*tc = tx;
numtexinfo++;
ThreadUnlock ();
return i;
}