forked from a1batross/Paranoia2_original
674 lines
16 KiB
C++
674 lines
16 KiB
C++
/***
|
|
*
|
|
* Copyright (c) 1996-2002, 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"
|
|
|
|
#define FWAD_USED BIT( 0 )
|
|
#define FWAD_INCLUDE BIT( 1 )
|
|
|
|
typedef struct
|
|
{
|
|
char name[64];
|
|
int flags;
|
|
} wadentry_t;
|
|
|
|
static const vec3_t baseaxis[18] =
|
|
{
|
|
{ 0, 0, 1}, { 1, 0, 0}, { 0,-1, 0}, // floor
|
|
{ 0, 0,-1}, { 1, 0, 0}, { 0,-1, 0}, // ceiling
|
|
{ 1, 0, 0}, { 0, 1, 0}, { 0, 0,-1}, // west wall
|
|
{-1, 0, 0}, { 0, 1, 0}, { 0, 0,-1}, // east wall
|
|
{ 0, 1, 0}, { 1, 0, 0}, { 0, 0,-1}, // south wall
|
|
{ 0,-1, 0}, { 1, 0, 0}, { 0, 0,-1}, // north wall
|
|
};
|
|
|
|
static mipentry_t g_miptex[MAX_MAP_TEXTURES];
|
|
static int g_nummiptex;
|
|
static wadentry_t g_wadlist[MAX_TEXFILES];
|
|
static int g_wadcount;
|
|
char g_pszWadInclude[MAX_TEXFILES][64];
|
|
int g_nWadInclude;
|
|
|
|
void TEX_LoadTextures( CUtlArray<mapent_t> *entities, bool merge )
|
|
{
|
|
char wadstring[MAX_VALUE];
|
|
char tmpWadName[64];
|
|
char *pszWadString;
|
|
char *pszWadFile;
|
|
int startcount;
|
|
|
|
if( !merge )
|
|
{
|
|
memset( g_wadlist, 0 , sizeof( g_wadlist ));
|
|
memset( g_miptex, 0 , sizeof( g_miptex ));
|
|
g_wadcount = g_nummiptex = 0;
|
|
}
|
|
|
|
if( entities->Count() <= 0 )
|
|
return;
|
|
|
|
pszWadString = ValueForKey( (entity_t *)&entities[0], "wad" );
|
|
if( !*pszWadString ) pszWadString = ValueForKey( (entity_t *)&entities[0], "_wad" );
|
|
if( !*pszWadString ) return; // may be -nowadtextures was used?
|
|
startcount = g_wadcount;
|
|
|
|
Q_strncpy( wadstring, pszWadString, MAX_VALUE - 2 );
|
|
wadstring[MAX_VALUE-1] = 0;
|
|
|
|
if( !Q_strchr( wadstring, ';' ))
|
|
Q_strcat( wadstring, ";" );
|
|
|
|
// parse wad pathes
|
|
for( pszWadFile = strtok( wadstring, ";" ); pszWadFile != NULL; pszWadFile = strtok( NULL, ";" ))
|
|
{
|
|
COM_FixSlashes( pszWadFile );
|
|
COM_FileBase( pszWadFile, tmpWadName );
|
|
|
|
// check for duplicate
|
|
for( int i = 0; i < g_wadcount; i++ )
|
|
{
|
|
if( !Q_stricmp( g_wadlist[i].name, tmpWadName ))
|
|
break;
|
|
}
|
|
|
|
if( i != g_wadcount )
|
|
continue; // already included
|
|
|
|
Q_strncpy( g_wadlist[g_wadcount].name, tmpWadName, sizeof( g_wadlist[0].name ));
|
|
|
|
if( ++g_wadcount >= MAX_TEXFILES )
|
|
break; // too many wads...
|
|
}
|
|
|
|
// set wads that should be included...
|
|
for( int i = startcount; i < g_wadcount; i++ )
|
|
{
|
|
if( g_wadtextures )
|
|
{
|
|
for( int j = 0; j < g_nWadInclude; j++ )
|
|
{
|
|
if( !Q_stricmp( g_wadlist[i].name, g_pszWadInclude[j] ))
|
|
{
|
|
MsgDev( D_INFO, "Including WAD File: %s\n", g_wadlist[i].name );
|
|
SetBits( g_wadlist[i].flags, FWAD_INCLUDE );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// force to include all the wads
|
|
SetBits( g_wadlist[i].flags, FWAD_INCLUDE );
|
|
}
|
|
}
|
|
}
|
|
|
|
bool TEX_CheckMiptex( const char *name )
|
|
{
|
|
char texname[64];
|
|
|
|
Q_snprintf( texname, sizeof( texname ), "%s.mip", name );
|
|
|
|
// check wads in reverse order
|
|
for( int i = g_wadcount - 1; i >= 0; i-- )
|
|
{
|
|
char *texpath = va( "%s.wad/%s", g_wadlist[i].name, texname );
|
|
|
|
if( FS_FileExists( texpath, false ))
|
|
return true;
|
|
}
|
|
|
|
// maybe it's tga path?
|
|
Q_snprintf( texname, sizeof( texname ), "textures/%s.tga", name );
|
|
|
|
if( FS_FileExists( texname, false ))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool TEX_LoadMiptex( const char *name, mipentry_t *tex )
|
|
{
|
|
char texname[64];
|
|
shaderInfo_t *shader;
|
|
|
|
Q_snprintf( texname, sizeof( texname ), "%s.mip", name );
|
|
Q_strncpy( tex->name, name, sizeof( tex->name ));
|
|
tex->width = tex->height = 16; // same as "*default"
|
|
|
|
// check wads in reverse order
|
|
for( int i = g_wadcount - 1; i >= 0; i-- )
|
|
{
|
|
char *texpath = va( "%s.wad/%s", g_wadlist[i].name, texname );
|
|
|
|
if( FS_FileExists( texpath, false ))
|
|
{
|
|
// texture is loaded, wad is used
|
|
SetBits( g_wadlist[i].flags, FWAD_USED );
|
|
tex->data = FS_LoadFile( texpath, &tex->datasize, false );
|
|
tex->width = ((miptex_t *)tex->data)->width;
|
|
tex->height = ((miptex_t *)tex->data)->height;
|
|
tex->wadnum = i; // wad where the tex is located
|
|
return true;
|
|
}
|
|
}
|
|
#if 0 // this is no longer support by engine
|
|
// wadpath is not specified
|
|
if( FS_FileExists( texname, false ))
|
|
{
|
|
// texture is loaded, wad is used
|
|
tex->data = FS_LoadFile( texname, &tex->datasize, false );
|
|
tex->width = ((miptex_t *)tex->data)->width;
|
|
tex->height = ((miptex_t *)tex->data)->height;
|
|
tex->wadnum = -1; // wad where the tex is located
|
|
return true;
|
|
}
|
|
#endif
|
|
shader = ShaderInfoForShader( name );
|
|
|
|
// maybe it's tga path?
|
|
if( FS_FileExists( shader->imagePath, false ))
|
|
{
|
|
// texture is loaded, wad is not used
|
|
TEX_LoadTGA( shader->imagePath, tex );
|
|
|
|
// FIXME: add loader for dds
|
|
return true;
|
|
}
|
|
|
|
MsgDev( D_WARN, "%s failed to load\n", name );
|
|
return false;
|
|
}
|
|
|
|
int TEX_FindMiptex( const char *name )
|
|
{
|
|
ThreadLock ();
|
|
|
|
// see if already loaded
|
|
for( int i = 0; i < g_nummiptex; i++ )
|
|
{
|
|
if( !Q_stricmp( name, g_miptex[i].name ))
|
|
{
|
|
ThreadUnlock();
|
|
return i;
|
|
}
|
|
}
|
|
|
|
if( g_nummiptex == MAX_MAP_TEXTURES )
|
|
COM_FatalError( "MAX_MAP_TEXTURES limit exceeded\n" );
|
|
|
|
TEX_LoadMiptex( name, &g_miptex[i] );
|
|
g_nummiptex++;
|
|
ThreadUnlock ();
|
|
|
|
return i;
|
|
}
|
|
|
|
void TEX_GetSize( const char *name, int *width, int *height )
|
|
{
|
|
int miptex = TEX_FindMiptex( name );
|
|
|
|
*width = g_miptex[miptex].width;
|
|
*height = g_miptex[miptex].height;
|
|
}
|
|
|
|
void TEX_FreeTextures( void )
|
|
{
|
|
// purge texture cache
|
|
for( int i = 0; i < g_nummiptex; i++ )
|
|
{
|
|
Mem_Free( g_miptex[i].data, C_FILESYSTEM );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
lump_sorters
|
|
=================
|
|
*/
|
|
static int lump_sorter_by_wad_and_name( const void *lump1, const void *lump2 )
|
|
{
|
|
mipentry_t *plump1 = (mipentry_t *)lump1;
|
|
mipentry_t *plump2 = (mipentry_t *)lump2;
|
|
|
|
if( plump1->wadnum == plump2->wadnum )
|
|
return Q_stricmp( plump1->name, plump2->name );
|
|
else return plump1->wadnum - plump2->wadnum;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
LoadLump
|
|
==================
|
|
*/
|
|
int LoadLump( mipentry_t *source, byte *dest )
|
|
{
|
|
if( source->datasize )
|
|
{
|
|
// should we just load the texture header w/o the palette & bitmap?
|
|
if( source->wadnum == -1 || !FBitSet( g_wadlist[source->wadnum].flags, FWAD_INCLUDE ))
|
|
{
|
|
// Just read the miptex header and zero out the data offsets.
|
|
// We will load the entire texture from the WAD at engine runtime
|
|
miptex_t *miptex = (miptex_t *)dest;
|
|
|
|
Q_strncpy( miptex->name, source->name, WAD3_NAMELEN );
|
|
miptex->width = source->width;
|
|
miptex->height = source->height;
|
|
for( int 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
|
|
memcpy( dest, source->data, source->datasize );
|
|
return source->datasize;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
LumpSize
|
|
|
|
returns the size of lump
|
|
==================
|
|
*/
|
|
int LumpSize( const mipentry_t *source )
|
|
{
|
|
if( source->datasize )
|
|
{
|
|
// should we just load the texture header w/o the palette & bitmap?
|
|
if( source->wadnum == -1 || !FBitSet( g_wadlist[source->wadnum].flags, FWAD_INCLUDE ))
|
|
return sizeof( miptex_t );
|
|
return source->datasize;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
AddAnimatingTextures
|
|
==================
|
|
*/
|
|
void AddAnimatingTextures( void )
|
|
{
|
|
int base = g_nummiptex;
|
|
char name[64];
|
|
|
|
for( int i = 0; i < base; i++ )
|
|
{
|
|
if( g_miptex[i].name[0] != '+' && g_miptex[i].name[0] != '-' )
|
|
continue;
|
|
Q_strcpy( name, g_miptex[i].name );
|
|
|
|
for( int 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
|
|
if( TEX_CheckMiptex( name ))
|
|
{
|
|
// add to the miptex list
|
|
TEX_FindMiptex( name );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( g_nummiptex - base )
|
|
MsgDev( D_REPORT, "added %i additional animating textures.\n", g_nummiptex - base );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CheckSpecialTexture
|
|
==================
|
|
*/
|
|
bool CheckSpecialTexture( const char *name )
|
|
{
|
|
if( !Q_stricmp( name, "NULL" ))
|
|
return true;
|
|
if( !Q_stricmp( name, "SKIP" ))
|
|
return true;
|
|
if( !Q_stricmp( name, "HINT" ))
|
|
return true;
|
|
if( !Q_stricmp( name, "SOLIDHINT" ))
|
|
return true;
|
|
if( !Q_stricmp( name, "SKY" ))
|
|
return true;
|
|
if( !Q_stricmp( name, "ORIGIN" ))
|
|
return true;
|
|
if( !Q_stricmp( name, "CLIP" ))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
WriteMiptex
|
|
==================
|
|
*/
|
|
void WriteMiptex( void )
|
|
{
|
|
dtexinfo_t *tx = g_texinfo;
|
|
char szTmpWad[1024];
|
|
int i, len;
|
|
byte *data;
|
|
dmiptexlump_t *l;
|
|
|
|
g_texdatasize = 0;
|
|
szTmpWad[0] = 0;
|
|
|
|
// add animating textures finally
|
|
AddAnimatingTextures();
|
|
|
|
// sort them FIRST by wadfile and THEN by name for most efficient loading in the engine.
|
|
qsort( (void *)g_miptex, (size_t)g_nummiptex, sizeof( g_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 < g_numtexinfo; i++, tx++ )
|
|
{
|
|
char *miptex_name = (char *)tx->miptex;
|
|
|
|
tx->miptex = TEX_FindMiptex( miptex_name );
|
|
|
|
// free up the originally strdup()'ed miptex_name
|
|
free( miptex_name );
|
|
}
|
|
|
|
int totaldatasize = sizeof( int ) + ( sizeof( int ) * g_nummiptex );
|
|
|
|
for( i = 0; i < g_nummiptex; i++ )
|
|
{
|
|
len = LumpSize( g_miptex + i );
|
|
totaldatasize += len;
|
|
}
|
|
|
|
Msg( "total texture data %s\n", Q_memprint( totaldatasize ));
|
|
g_dtexdata = (byte *)Mem_Alloc( totaldatasize );
|
|
|
|
if( totaldatasize > MAX_MAP_MIPTEX )
|
|
MsgDev( D_WARN, "MAX_MAP_MIPTEX limit overflow\n" );
|
|
|
|
// now setup to get the miptex data (or just the headers if using -wadtextures) from the wadfile
|
|
l = (dmiptexlump_t *)g_dtexdata;
|
|
data = (byte *)&l->dataofs[g_nummiptex];
|
|
l->nummiptex = g_nummiptex;
|
|
|
|
for( i = 0; i < g_nummiptex; i++ )
|
|
{
|
|
l->dataofs[i] = data - (byte *)l;
|
|
len = LoadLump( g_miptex + i, data );
|
|
if( !len ) l->dataofs[i] = -1; // didn't find the texture
|
|
data += len;
|
|
}
|
|
|
|
g_texdatasize = data - g_dtexdata;
|
|
|
|
if( totaldatasize != g_texdatasize )
|
|
COM_FatalError( "WriteMiptex: memory corrupted\n" );
|
|
|
|
for( i = 0; i < g_wadcount; i++ )
|
|
{
|
|
// all used textures from this wad will be include into map
|
|
if( FBitSet( g_wadlist[i].flags, FWAD_INCLUDE ))
|
|
continue;
|
|
|
|
// nothing used from this wad
|
|
if( !FBitSet( g_wadlist[i].flags, FWAD_USED ))
|
|
continue;
|
|
|
|
Q_snprintf( szTmpWad, sizeof( szTmpWad ), "%s%s.wad;", szTmpWad, g_wadlist[i].name );
|
|
MsgDev( D_INFO, "Using WAD File: %s.wad\n", g_wadlist[i].name );
|
|
}
|
|
|
|
len = Q_strlen( szTmpWad );
|
|
|
|
// cutoff last semicolon
|
|
if( len > 0 && szTmpWad[Q_strlen( szTmpWad ) - 1] == ';' )
|
|
szTmpWad[len-1] = '\0';
|
|
|
|
if( *szTmpWad ) MsgDev( D_REPORT, "Wad files required to run the map: \"%s\"\n", szTmpWad );
|
|
else MsgDev( D_REPORT, "Wad files required to run the map: (None)\n" );
|
|
|
|
// update 'wad' field
|
|
SetKeyValue( (entity_t *)&g_mapentities[0], "wad", szTmpWad );
|
|
}
|
|
|
|
// =====================================================================================
|
|
// FaceinfoForTexinfo
|
|
// =====================================================================================
|
|
short FaceinfoForTexinfo( const char *landname, const int in_texture_step, const int in_max_extent, const int groupid )
|
|
{
|
|
byte texture_step = bound( MIN_CUSTOM_TEXTURE_STEP, in_texture_step, MAX_CUSTOM_TEXTURE_STEP );
|
|
byte max_extent = bound( MIN_CUSTOM_SURFACE_EXTENT, in_max_extent, MAX_CUSTOM_SURFACE_EXTENT );
|
|
dfaceinfo_t *fi = g_dfaceinfo;
|
|
int i;
|
|
|
|
ThreadLock();
|
|
|
|
for( i = 0; i < g_numfaceinfo; i++, fi++ )
|
|
{
|
|
if( Q_stricmp( landname, fi->landname ))
|
|
continue;
|
|
|
|
if( fi->texture_step != texture_step )
|
|
continue;
|
|
|
|
if( fi->max_extent != max_extent )
|
|
continue;
|
|
|
|
if( fi->groupid != groupid )
|
|
continue;
|
|
|
|
ThreadUnlock();
|
|
return i;
|
|
}
|
|
|
|
if( g_numfaceinfo == MAX_MAP_FACEINFO )
|
|
COM_FatalError( "MAX_MAP_FACEINFO limit exceeded\n" );
|
|
|
|
// allocate tne entry
|
|
Q_strncpy( fi->landname, landname, sizeof( fi->landname ));
|
|
fi->texture_step = texture_step;
|
|
fi->max_extent = max_extent;
|
|
fi->groupid = groupid;
|
|
g_numfaceinfo++;
|
|
|
|
ThreadUnlock();
|
|
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
ComputeAxisBase
|
|
==================
|
|
*/
|
|
void ComputeAxisBase( vec3_t normal, vec3_t texX, vec3_t texY )
|
|
{
|
|
vec_t RotY, RotZ;
|
|
|
|
if( fabs( normal[0] ) < 1e-6 )
|
|
normal[0] = 0.0f;
|
|
|
|
if( fabs( normal[1] ) < 1e-6 )
|
|
normal[1] = 0.0f;
|
|
|
|
if( fabs( normal[2] ) < 1e-6 )
|
|
normal[2] = 0.0f;
|
|
|
|
// compute the two rotations around y and z to rotate x to normal
|
|
RotY = -atan2( normal[2], sqrt( normal[1] * normal[1] + normal[0] * normal[0] ));
|
|
RotZ = atan2( normal[1], normal[0] );
|
|
|
|
// rotate (0,1,0) and (0,0,1) to compute texX and texY
|
|
texX[0] = -sin( RotZ );
|
|
texX[1] = cos( RotZ );
|
|
texX[2] = 0;
|
|
|
|
// the texY vector is along -z (t texture coorinates axis)
|
|
texY[0] = -sin( RotY ) * cos( RotZ );
|
|
texY[1] = -sin( RotY ) * sin( RotZ );
|
|
texY[2] = -cos( RotY );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
TextureAxisFromPlane
|
|
==================
|
|
*/
|
|
void TextureAxisFromNormal( vec3_t normal, vec3_t xv, vec3_t yv, bool brush_primitive )
|
|
{
|
|
vec_t dot, best = 0.0;
|
|
int bestaxis = 0;
|
|
|
|
if( brush_primitive )
|
|
{
|
|
ComputeAxisBase( normal, xv, yv );
|
|
return;
|
|
}
|
|
|
|
for( int i = 0; i < 6; i++ )
|
|
{
|
|
dot = DotProduct( normal, baseaxis[i*3+0] );
|
|
|
|
if( dot > best + 0.0001 )
|
|
{
|
|
bestaxis = i;
|
|
best = dot;
|
|
}
|
|
}
|
|
|
|
VectorCopy( baseaxis[bestaxis*3+1], xv );
|
|
VectorCopy( baseaxis[bestaxis*3+2], yv );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
TextureAxisFromPlane
|
|
==================
|
|
*/
|
|
void TextureAxisFromSide( const side_t *side, vec3_t xv, vec3_t yv, bool brush_primitive )
|
|
{
|
|
vec3_t t1, t2, normal;
|
|
|
|
VectorSubtract( side->planepts[0], side->planepts[1], t1 );
|
|
VectorSubtract( side->planepts[2], side->planepts[1], t2 );
|
|
CrossProduct( t1, t2, normal );
|
|
VectorNormalize( normal );
|
|
TextureAxisFromNormal( normal, xv, yv, brush_primitive );
|
|
}
|
|
|
|
int TexinfoForSide( plane_t *plane, side_t *s, const vec3_t origin )
|
|
{
|
|
vec_t vecs[2][4];
|
|
dtexinfo_t tx, *tc;
|
|
int i, j, k;
|
|
|
|
if( FBitSet( s->flags, FSIDE_NODRAW|FSIDE_SKIP ))
|
|
return -1;
|
|
|
|
memset( &tx, 0, sizeof( tx ));
|
|
tx.miptex = TEX_FindMiptex( s->name );
|
|
// Note: FindMiptex() still needs to be called here to add it to the global miptex array
|
|
tx.faceinfo = s->faceinfo;
|
|
|
|
if( FBitSet( s->flags, FSIDE_NOLIGHTMAP ))
|
|
SetBits( tx.flags, TEX_SPECIAL );
|
|
|
|
if( FBitSet( s->flags, FSIDE_NOSHADOW ))
|
|
SetBits( tx.flags, TEX_NOSHADOW );
|
|
|
|
if( FBitSet( s->flags, FSIDE_NODIRT ))
|
|
SetBits( tx.flags, TEX_NODIRT );
|
|
|
|
if( FBitSet( s->flags, FSIDE_SCROLL ))
|
|
SetBits( tx.flags, TEX_SCROLL );
|
|
|
|
if( !FBitSet( s->flags, FSIDE_PATCH ))
|
|
{
|
|
if( g_world_luxels >= 1 )
|
|
SetBits( tx.flags, TEX_WORLD_LUXELS );
|
|
|
|
if( g_world_luxels >= 2 )
|
|
SetBits( tx.flags, TEX_AXIAL_LUXELS );
|
|
}
|
|
|
|
// prepare the source vecs
|
|
memcpy( vecs, s->vecs, sizeof( vecs ));
|
|
|
|
// add the origin offset
|
|
if( origin[0] || origin[1] || origin[2] )
|
|
{
|
|
vecs[0][3] += DotProduct( origin, vecs[0] );
|
|
vecs[1][3] += DotProduct( origin, vecs[1] );
|
|
}
|
|
|
|
// can't use memcpy because size can be mismatched
|
|
for( i = 0; i < 2; i++ )
|
|
{
|
|
for( j = 0; j < 4; j++ )
|
|
{
|
|
tx.vecs[i][j] = vecs[i][j];
|
|
}
|
|
}
|
|
|
|
//
|
|
// find the texinfo
|
|
//
|
|
ThreadLock();
|
|
tc = g_texinfo;
|
|
|
|
for( i = 0; i < g_numtexinfo; i++, tc++ )
|
|
{
|
|
// Sleazy hack 104, Pt 3 - Use strcmp on names to avoid dups
|
|
if( Q_stricmp( (char *)tc->miptex, s->name ))
|
|
continue;
|
|
|
|
if( tc->flags != tx.flags )
|
|
continue;
|
|
|
|
if( tc->faceinfo != tx.faceinfo )
|
|
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( g_numtexinfo == MAX_MAP_TEXINFO )
|
|
COM_FatalError( "MAX_MAP_TEXINFO limit exceeded\n" );
|
|
|
|
// 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().
|
|
// g-cont. do it only for unique elements, but i can't use copysting here because it may call ThreadLock again
|
|
tx.miptex = (int)strdup( s->name );
|
|
|
|
*tc = tx;
|
|
g_numtexinfo++;
|
|
ThreadUnlock ();
|
|
|
|
return i;
|
|
} |