DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

Model.cpp (69806B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #pragma hdrstop
     30 #include "../idlib/precompiled.h"
     31 
     32 
     33 #include "tr_local.h"
     34 #include "Model_local.h"
     35 #include "Model_ase.h"
     36 #include "Model_lwo.h"
     37 #include "Model_ma.h"
     38 
     39 idCVar idRenderModelStatic::r_mergeModelSurfaces( "r_mergeModelSurfaces", "1", CVAR_BOOL|CVAR_RENDERER, "combine model surfaces with the same material" );
     40 idCVar idRenderModelStatic::r_slopVertex( "r_slopVertex", "0.01", CVAR_RENDERER, "merge xyz coordinates this far apart" );
     41 idCVar idRenderModelStatic::r_slopTexCoord( "r_slopTexCoord", "0.001", CVAR_RENDERER, "merge texture coordinates this far apart" );
     42 idCVar idRenderModelStatic::r_slopNormal( "r_slopNormal", "0.02", CVAR_RENDERER, "merge normals that dot less than this" );
     43 
     44 static const byte BRM_VERSION = 108;
     45 static const unsigned int BRM_MAGIC = ( 'B' << 24 ) | ( 'R' << 16 ) | ( 'M' << 8 ) | BRM_VERSION;
     46 
     47 /*
     48 ================
     49 idRenderModelStatic::idRenderModelStatic
     50 ================
     51 */
     52 idRenderModelStatic::idRenderModelStatic() {
     53 	name = "<undefined>";
     54 	bounds.Clear();
     55 	lastModifiedFrame = 0;
     56 	lastArchivedFrame = 0;
     57 	overlaysAdded = 0;
     58 	isStaticWorldModel = false;
     59 	defaulted = false;
     60 	purged = false;
     61 	fastLoad = false;
     62 	reloadable = true;
     63 	levelLoadReferenced = false;
     64 	hasDrawingSurfaces = true;
     65 	hasInteractingSurfaces = true;
     66 	hasShadowCastingSurfaces = true;
     67 	timeStamp = 0;
     68 	numInvertedJoints = 0;
     69 	jointsInverted = NULL;
     70 	jointsInvertedBuffer = 0;
     71 }
     72 
     73 /*
     74 ================
     75 idRenderModelStatic::~idRenderModelStatic
     76 ================
     77 */
     78 idRenderModelStatic::~idRenderModelStatic() {
     79 	PurgeModel();
     80 }
     81 
     82 /*
     83 ==============
     84 idRenderModelStatic::Print
     85 ==============
     86 */
     87 void idRenderModelStatic::Print() const {
     88 	common->Printf( "%s\n", name.c_str() );
     89 	common->Printf( "Static model.\n" );
     90 	common->Printf( "bounds: (%f %f %f) to (%f %f %f)\n", 
     91 		bounds[0][0], bounds[0][1], bounds[0][2], 
     92 		bounds[1][0], bounds[1][1], bounds[1][2] );
     93 
     94 	common->Printf( "    verts  tris material\n" );
     95 	for ( int i = 0; i < NumSurfaces(); i++ ) {
     96 		const modelSurface_t	*surf = Surface( i );
     97 
     98 		srfTriangles_t *tri = surf->geometry;
     99 		const idMaterial *material = surf->shader;
    100 		
    101 		if ( !tri ) {
    102 			common->Printf( "%2i: %s, NULL surface geometry\n", i, material->GetName() );
    103 			continue;
    104 		}
    105 
    106 		common->Printf( "%2i: %5i %5i %s", i, tri->numVerts, tri->numIndexes / 3, material->GetName() );
    107 		if ( tri->generateNormals ) {
    108 			common->Printf( " (smoothed)\n" );
    109 		} else {
    110 			common->Printf( "\n" );
    111 		}
    112 	}	
    113 }
    114 
    115 /*
    116 ==============
    117 idRenderModelStatic::Memory
    118 ==============
    119 */
    120 int idRenderModelStatic::Memory() const {
    121 	int	totalBytes = 0;
    122 
    123 	totalBytes += sizeof( *this );
    124 	totalBytes += name.DynamicMemoryUsed();
    125 	totalBytes += surfaces.MemoryUsed();
    126 
    127 	for ( int j = 0; j < NumSurfaces(); j++ ) {
    128 		const modelSurface_t	*surf = Surface( j );
    129 		if ( !surf->geometry ) {
    130 			continue;
    131 		}
    132 		totalBytes += R_TriSurfMemory( surf->geometry );
    133 	}
    134 
    135 	return totalBytes;
    136 }
    137 
    138 /*
    139 ==============
    140 idRenderModelStatic::List
    141 ==============
    142 */
    143 void idRenderModelStatic::List() const {
    144 	int	totalTris = 0;
    145 	int	totalVerts = 0;
    146 	int	totalBytes = 0;
    147 
    148 	totalBytes = Memory();
    149 
    150 	char	closed = 'C';
    151 	for ( int j = 0; j < NumSurfaces(); j++ ) {
    152 		const modelSurface_t	*surf = Surface( j );
    153 		if ( !surf->geometry ) {
    154 			continue;
    155 		}
    156 		if ( !surf->geometry->perfectHull ) {
    157 			closed = ' ';
    158 		}
    159 		totalTris += surf->geometry->numIndexes / 3;
    160 		totalVerts += surf->geometry->numVerts;
    161 	}
    162 	common->Printf( "%c%4ik %3i %4i %4i %s", closed, totalBytes/1024, NumSurfaces(), totalVerts, totalTris, Name() );
    163 
    164 	if ( IsDynamicModel() == DM_CACHED ) {
    165 		common->Printf( " (DM_CACHED)" );
    166 	}
    167 	if ( IsDynamicModel() == DM_CONTINUOUS ) {
    168 		common->Printf( " (DM_CONTINUOUS)" );
    169 	}
    170 	if ( defaulted ) {
    171 		common->Printf( " (DEFAULTED)" );
    172 	}
    173 	if ( bounds[0][0] >= bounds[1][0] ) {
    174 		common->Printf( " (EMPTY BOUNDS)" );
    175 	}
    176 	if ( bounds[1][0] - bounds[0][0] > 100000 ) {
    177 		common->Printf( " (HUGE BOUNDS)" );
    178 	}
    179 
    180 	common->Printf( "\n" );
    181 }
    182 
    183 /*
    184 ================
    185 idRenderModelStatic::IsDefaultModel
    186 ================
    187 */
    188 bool idRenderModelStatic::IsDefaultModel() const {
    189 	return defaulted;
    190 }
    191 
    192 /*
    193 ================
    194 AddCubeFace
    195 ================
    196 */
    197 static void AddCubeFace( srfTriangles_t *tri, idVec3 v1, idVec3 v2, idVec3 v3, idVec3 v4 ) {
    198 	tri->verts[tri->numVerts+0].Clear();
    199 	tri->verts[tri->numVerts+0].xyz = v1 * 8;
    200 	tri->verts[tri->numVerts+0].SetTexCoord( 0, 0 );
    201 
    202 	tri->verts[tri->numVerts+1].Clear();
    203 	tri->verts[tri->numVerts+1].xyz = v2 * 8;
    204 	tri->verts[tri->numVerts+1].SetTexCoord( 1, 0 );
    205 
    206 	tri->verts[tri->numVerts+2].Clear();
    207 	tri->verts[tri->numVerts+2].xyz = v3 * 8;
    208 	tri->verts[tri->numVerts+2].SetTexCoord( 1, 1 );
    209 
    210 	tri->verts[tri->numVerts+3].Clear();
    211 	tri->verts[tri->numVerts+3].xyz = v4 * 8;
    212 	tri->verts[tri->numVerts+3].SetTexCoord( 0, 1 );
    213 
    214 	tri->indexes[tri->numIndexes+0] = tri->numVerts + 0;
    215 	tri->indexes[tri->numIndexes+1] = tri->numVerts + 1;
    216 	tri->indexes[tri->numIndexes+2] = tri->numVerts + 2;
    217 	tri->indexes[tri->numIndexes+3] = tri->numVerts + 0;
    218 	tri->indexes[tri->numIndexes+4] = tri->numVerts + 2;
    219 	tri->indexes[tri->numIndexes+5] = tri->numVerts + 3;
    220 
    221 	tri->numVerts += 4;
    222 	tri->numIndexes += 6;
    223 }
    224 
    225 /*
    226 ================
    227 idRenderModelStatic::MakeDefaultModel
    228 ================
    229 */
    230 void idRenderModelStatic::MakeDefaultModel() {
    231 
    232 	defaulted = true;
    233 
    234 	// throw out any surfaces we already have
    235 	PurgeModel();
    236 
    237 	// create one new surface
    238 	modelSurface_t	surf;
    239 
    240 	srfTriangles_t *tri = R_AllocStaticTriSurf();
    241 
    242 	surf.shader = tr.defaultMaterial;
    243 	surf.geometry = tri;
    244 
    245 	R_AllocStaticTriSurfVerts( tri, 24 );
    246 	R_AllocStaticTriSurfIndexes( tri, 36 );
    247 
    248 	AddCubeFace( tri, idVec3(-1, 1, 1), idVec3(1, 1, 1), idVec3(1, -1, 1), idVec3(-1, -1, 1) );
    249 	AddCubeFace( tri, idVec3(-1, 1, -1), idVec3(-1, -1, -1), idVec3(1, -1, -1), idVec3(1, 1, -1) );
    250 
    251 	AddCubeFace( tri, idVec3(1, -1, 1), idVec3(1, 1, 1), idVec3(1, 1, -1), idVec3(1, -1, -1) );
    252 	AddCubeFace( tri, idVec3(-1, -1, 1), idVec3(-1, -1, -1), idVec3(-1, 1, -1), idVec3(-1, 1, 1) );
    253 
    254 	AddCubeFace( tri, idVec3(-1, -1, 1), idVec3(1, -1, 1), idVec3(1, -1, -1), idVec3(-1, -1, -1) );
    255 	AddCubeFace( tri, idVec3(-1, 1, 1), idVec3(-1, 1, -1), idVec3(1, 1, -1), idVec3(1, 1, 1) );
    256 
    257 	tri->generateNormals = true;
    258 
    259 	AddSurface( surf );
    260 	FinishSurfaces();
    261 }
    262 
    263 /*
    264 ================
    265 idRenderModelStatic::PartialInitFromFile
    266 ================
    267 */
    268 void idRenderModelStatic::PartialInitFromFile( const char *fileName ) {
    269 	fastLoad = true;
    270 	InitFromFile( fileName );
    271 }
    272 
    273 /*
    274 ================
    275 idRenderModelStatic::InitFromFile
    276 ================
    277 */
    278 void idRenderModelStatic::InitFromFile( const char *fileName ) {
    279 	bool loaded;
    280 	idStr extension;
    281 
    282 	InitEmpty( fileName );
    283 
    284 	// FIXME: load new .proc map format
    285 
    286 	name.ExtractFileExtension( extension );
    287 
    288 	if ( extension.Icmp( "ase" ) == 0 ) {
    289 		loaded		= LoadASE( name );
    290 		reloadable	= true;
    291 	} else if ( extension.Icmp( "lwo" ) == 0 ) {
    292 		loaded		= LoadLWO( name );
    293 		reloadable	= true;
    294 	} else if ( extension.Icmp( "ma" ) == 0 ) {
    295 		loaded		= LoadMA( name );
    296 		reloadable	= true;
    297 	} else {
    298 		common->Warning( "idRenderModelStatic::InitFromFile: unknown type for model: \'%s\'", name.c_str() );
    299 		loaded		= false;
    300 	}
    301 
    302 	if ( !loaded ) {
    303 		common->Warning( "Couldn't load model: '%s'", name.c_str() );
    304 		MakeDefaultModel();
    305 		return;
    306 	}
    307 
    308 	// it is now available for use
    309 	purged = false;
    310 
    311 	// create the bounds for culling and dynamic surface creation
    312 	FinishSurfaces();
    313 }
    314 
    315 /*
    316 ========================
    317 idRenderModelStatic::LoadBinaryModel
    318 ========================
    319 */
    320 bool idRenderModelStatic::LoadBinaryModel( idFile * file, const ID_TIME_T sourceTimeStamp ) {
    321 	if ( file == NULL ) {
    322 		return false;
    323 	}
    324 
    325 	unsigned int magic = 0;
    326 	file->ReadBig( magic );
    327 	if ( magic != BRM_MAGIC ) {
    328 		return false;
    329 	}
    330 	
    331 	file->ReadBig( timeStamp );
    332 
    333 	if ( !fileSystem->InProductionMode() && sourceTimeStamp != timeStamp ) {
    334 		return false;
    335 	}
    336 
    337 	common->UpdateLevelLoadPacifier();
    338 
    339 	int numSurfaces;
    340 	file->ReadBig( numSurfaces );
    341 	surfaces.SetNum( numSurfaces );
    342 	for ( int i = 0; i < surfaces.Num(); i++ ) {
    343 		file->ReadBig( surfaces[i].id );
    344 		idStr materialName;
    345 		file->ReadString( materialName );
    346 		if ( materialName.IsEmpty() ) {
    347 			surfaces[i].shader = NULL;
    348 		} else {
    349 			surfaces[i].shader = declManager->FindMaterial( materialName );
    350 		}
    351 
    352 		bool isGeometry;
    353 		file->ReadBig( isGeometry );
    354 		surfaces[i].geometry = NULL;
    355 		if ( isGeometry ) {
    356 			bool temp;
    357 
    358 			surfaces[i].geometry = R_AllocStaticTriSurf();
    359 			
    360 			// Read the contents of srfTriangles_t
    361 			srfTriangles_t & tri = *surfaces[i].geometry;
    362 
    363 			file->ReadVec3( tri.bounds[0] );
    364 			file->ReadVec3( tri.bounds[1] );
    365 
    366 			int ambientViewCount = 0;	// FIXME: remove
    367 			file->ReadBig( ambientViewCount );
    368 			file->ReadBig( tri.generateNormals );
    369 			file->ReadBig( tri.tangentsCalculated );
    370 			file->ReadBig( tri.perfectHull );
    371 			file->ReadBig( tri.referencedIndexes );
    372 
    373 			file->ReadBig( tri.numVerts );
    374 			tri.verts = NULL;
    375 			int numInFile = 0;
    376 			file->ReadBig( numInFile );
    377 			if ( numInFile > 0 ) {
    378 				R_AllocStaticTriSurfVerts( &tri, tri.numVerts );
    379 				assert( tri.verts != NULL );
    380 				for ( int j = 0; j < tri.numVerts; j++ ) {
    381 					file->ReadVec3( tri.verts[j].xyz );
    382 					file->ReadBigArray( tri.verts[j].st, 2 );
    383 					file->ReadBigArray( tri.verts[j].normal, 4 );
    384 					file->ReadBigArray( tri.verts[j].tangent, 4 );
    385 					file->ReadBigArray( tri.verts[j].color, sizeof( tri.verts[j].color ) / sizeof( tri.verts[j].color[0] ) );
    386 					file->ReadBigArray( tri.verts[j].color2, sizeof( tri.verts[j].color2 ) / sizeof( tri.verts[j].color2[0] ) );
    387 				}
    388 			}
    389 
    390 			file->ReadBig( numInFile );
    391 			if ( numInFile == 0 ) {
    392 				tri.preLightShadowVertexes = NULL;
    393 			} else {
    394 				R_AllocStaticTriSurfPreLightShadowVerts( &tri, numInFile );
    395 				for ( int j = 0; j < numInFile; j++ ) {
    396 					file->ReadVec4( tri.preLightShadowVertexes[ j ].xyzw );
    397 				}
    398 			} 
    399 
    400 			file->ReadBig( tri.numIndexes );
    401 			tri.indexes = NULL;
    402 			tri.silIndexes = NULL;
    403 			if (  tri.numIndexes > 0 ) {
    404 				R_AllocStaticTriSurfIndexes( &tri, tri.numIndexes );
    405 				file->ReadBigArray( tri.indexes, tri.numIndexes );
    406 			}
    407 			file->ReadBig( numInFile );
    408 			if ( numInFile > 0 ) {
    409 				R_AllocStaticTriSurfSilIndexes( &tri, tri.numIndexes );
    410 				file->ReadBigArray( tri.silIndexes, tri.numIndexes );
    411 			}
    412 
    413 			file->ReadBig( tri.numMirroredVerts );
    414 			tri.mirroredVerts = NULL;
    415 			if ( tri.numMirroredVerts > 0 ) {
    416 				R_AllocStaticTriSurfMirroredVerts( &tri, tri.numMirroredVerts );
    417 				file->ReadBigArray( tri.mirroredVerts, tri.numMirroredVerts );
    418 			}
    419 
    420 			file->ReadBig( tri.numDupVerts );
    421 			tri.dupVerts = NULL;
    422 			if ( tri.numDupVerts > 0 ) {
    423 				R_AllocStaticTriSurfDupVerts( &tri, tri.numDupVerts );
    424 				file->ReadBigArray( tri.dupVerts, tri.numDupVerts * 2 );
    425 			}
    426 
    427 			file->ReadBig( tri.numSilEdges );
    428 			tri.silEdges = NULL;
    429 			if ( tri.numSilEdges > 0 ) {
    430 				R_AllocStaticTriSurfSilEdges( &tri, tri.numSilEdges );
    431 				assert( tri.silEdges != NULL );
    432 				for ( int j = 0; j < tri.numSilEdges; j++ ) {
    433 					file->ReadBig( tri.silEdges[j].p1 );
    434 					file->ReadBig( tri.silEdges[j].p2 );
    435 					file->ReadBig( tri.silEdges[j].v1 );
    436 					file->ReadBig( tri.silEdges[j].v2 );
    437 				}
    438 			}
    439 
    440 			file->ReadBig( temp );
    441 			tri.dominantTris = NULL;
    442 			if ( temp ) {
    443 				R_AllocStaticTriSurfDominantTris( &tri, tri.numVerts );
    444 				assert( tri.dominantTris != NULL );
    445 				for ( int j = 0; j < tri.numVerts; j++ ) {
    446 					file->ReadBig( tri.dominantTris[j].v2 );
    447 					file->ReadBig( tri.dominantTris[j].v3 );
    448 					file->ReadFloat( tri.dominantTris[j].normalizationScale[0] );
    449 					file->ReadFloat( tri.dominantTris[j].normalizationScale[1] );
    450 					file->ReadFloat( tri.dominantTris[j].normalizationScale[2] );
    451 				}
    452 			}
    453 
    454 			file->ReadBig( tri.numShadowIndexesNoFrontCaps );
    455 			file->ReadBig( tri.numShadowIndexesNoCaps );
    456 			file->ReadBig( tri.shadowCapPlaneBits );
    457 
    458 			tri.ambientSurface = NULL;
    459 			tri.nextDeferredFree = NULL;
    460 			tri.indexCache = 0;
    461 			tri.ambientCache = 0;
    462 			tri.shadowCache = 0;
    463 		}
    464 	}
    465 
    466 	file->ReadVec3( bounds[0] );
    467 	file->ReadVec3( bounds[1] );
    468 
    469 	file->ReadBig( overlaysAdded );
    470 	file->ReadBig( lastModifiedFrame );
    471 	file->ReadBig( lastArchivedFrame );
    472 	file->ReadString( name );
    473 	file->ReadBig( isStaticWorldModel );
    474 	file->ReadBig( defaulted );
    475 	file->ReadBig( purged );
    476 	file->ReadBig( fastLoad );
    477 	file->ReadBig( reloadable );
    478 	file->ReadBig( levelLoadReferenced );		// should this actually be saved/loaded?
    479 	file->ReadBig( hasDrawingSurfaces );
    480 	file->ReadBig( hasInteractingSurfaces );
    481 	file->ReadBig( hasShadowCastingSurfaces );
    482 
    483 	return true;
    484 }
    485 
    486 /*
    487 ========================
    488 idRenderModelStatic::WriteBinaryModel
    489 ========================
    490 */
    491 void idRenderModelStatic::WriteBinaryModel( idFile * file, ID_TIME_T *_timeStamp ) const {
    492 	if ( file == NULL ) {
    493 		common->Printf( "Failed to WriteBinaryModel\n" );
    494 		return;
    495 	}
    496 	
    497 	file->WriteBig( BRM_MAGIC );
    498 
    499 	if ( _timeStamp != NULL ) {
    500 		file->WriteBig( *_timeStamp );
    501 	} else {
    502 		file->WriteBig( timeStamp );
    503 	}
    504 
    505 	file->WriteBig( surfaces.Num() );
    506 	for ( int i = 0; i < surfaces.Num(); i++ ) {
    507 		file->WriteBig( surfaces[i].id );
    508 		if ( surfaces[i].shader != NULL && surfaces[i].shader->GetName() != NULL ) {
    509 			file->WriteString( surfaces[i].shader->GetName() );
    510 		} else {
    511 			file->WriteString( "" );
    512 		}
    513 
    514 		file->WriteBig( surfaces[i].geometry != NULL );
    515 		if ( surfaces[i].geometry != NULL ) {
    516 			srfTriangles_t & tri = *surfaces[i].geometry;
    517 
    518 			file->WriteVec3( tri.bounds[0] );
    519 			file->WriteVec3( tri.bounds[1] );
    520 
    521 			int ambientViewCount = 0;	// FIXME: remove
    522 			file->WriteBig( ambientViewCount );
    523 			file->WriteBig( tri.generateNormals );
    524 			file->WriteBig( tri.tangentsCalculated );
    525 			file->WriteBig( tri.perfectHull );
    526 			file->WriteBig( tri.referencedIndexes );
    527 
    528 			// shadow models use numVerts but have no verts
    529 			file->WriteBig( tri.numVerts );
    530 			if ( tri.verts != NULL ) {
    531 				file->WriteBig( tri.numVerts );
    532 			} else {
    533 				file->WriteBig( ( int ) 0 );
    534 			}
    535 
    536 			if ( tri.numVerts > 0 && tri.verts != NULL ) {
    537 				for ( int j = 0; j < tri.numVerts; j++ ) {
    538 					file->WriteVec3( tri.verts[j].xyz );
    539 					file->WriteBigArray( tri.verts[j].st, 2 );
    540 					file->WriteBigArray( tri.verts[j].normal, 4 );
    541 					file->WriteBigArray( tri.verts[j].tangent, 4 );
    542 					file->WriteBigArray( tri.verts[j].color, sizeof( tri.verts[j].color ) / sizeof( tri.verts[j].color[0] ) );
    543 					file->WriteBigArray( tri.verts[j].color2, sizeof( tri.verts[j].color2 ) / sizeof( tri.verts[j].color2[0] ) );
    544 				}
    545 			}
    546 
    547 			if ( tri.preLightShadowVertexes != NULL ) {
    548 				file->WriteBig( tri.numVerts * 2 );
    549 				for ( int j = 0; j < tri.numVerts * 2; j++ ) {
    550 					file->WriteVec4( tri.preLightShadowVertexes[ j ].xyzw );
    551 				}
    552 			} else {
    553 				file->WriteBig( ( int ) 0 );
    554 			}
    555 
    556 			file->WriteBig( tri.numIndexes );
    557 
    558 			if ( tri.numIndexes > 0 ) {
    559 				file->WriteBigArray( tri.indexes, tri.numIndexes );
    560 			}
    561 
    562 			if ( tri.silIndexes != NULL ) {
    563 				file->WriteBig( tri.numIndexes );
    564 			} else {
    565 				file->WriteBig( ( int ) 0 );
    566 			}
    567 
    568 			if ( tri.numIndexes > 0 && tri.silIndexes != NULL ) {
    569 				file->WriteBigArray( tri.silIndexes, tri.numIndexes );
    570 			}
    571 
    572 			file->WriteBig( tri.numMirroredVerts );
    573 			if ( tri.numMirroredVerts > 0 ) {
    574 				file->WriteBigArray( tri.mirroredVerts, tri.numMirroredVerts );
    575 			}
    576 
    577 			file->WriteBig( tri.numDupVerts );
    578 			if ( tri.numDupVerts > 0 ) {
    579 				file->WriteBigArray( tri.dupVerts, tri.numDupVerts * 2 );
    580 			}
    581 
    582 			file->WriteBig( tri.numSilEdges );
    583 			if ( tri.numSilEdges > 0 ) {
    584 				for ( int j = 0; j < tri.numSilEdges; j++ ) {
    585 					file->WriteBig( tri.silEdges[j].p1 );
    586 					file->WriteBig( tri.silEdges[j].p2 );
    587 					file->WriteBig( tri.silEdges[j].v1 );
    588 					file->WriteBig( tri.silEdges[j].v2 );
    589 				}
    590 			}
    591 
    592 			file->WriteBig( tri.dominantTris != NULL );
    593 			if ( tri.dominantTris != NULL ) {
    594 				for ( int j = 0; j < tri.numVerts; j++ ) {
    595 					file->WriteBig( tri.dominantTris[j].v2 );
    596 					file->WriteBig( tri.dominantTris[j].v3 );
    597 					file->WriteFloat( tri.dominantTris[j].normalizationScale[0] );
    598 					file->WriteFloat( tri.dominantTris[j].normalizationScale[1] );
    599 					file->WriteFloat( tri.dominantTris[j].normalizationScale[2] );
    600 				}
    601 			}
    602 
    603 			file->WriteBig( tri.numShadowIndexesNoFrontCaps );
    604 			file->WriteBig( tri.numShadowIndexesNoCaps );
    605 			file->WriteBig( tri.shadowCapPlaneBits );
    606 		}
    607 	}
    608 
    609 	file->WriteVec3( bounds[0] );
    610 	file->WriteVec3( bounds[1] );
    611 	file->WriteBig( overlaysAdded );
    612 	file->WriteBig( lastModifiedFrame );
    613 	file->WriteBig( lastArchivedFrame );
    614 	file->WriteString( name );
    615 
    616 	// shadowHull
    617 
    618 	file->WriteBig( isStaticWorldModel );
    619 	file->WriteBig( defaulted );
    620 	file->WriteBig( purged );
    621 	file->WriteBig( fastLoad );
    622 	file->WriteBig( reloadable );
    623 	file->WriteBig( levelLoadReferenced );
    624 	file->WriteBig( hasDrawingSurfaces );
    625 	file->WriteBig( hasInteractingSurfaces );
    626 	file->WriteBig( hasShadowCastingSurfaces );
    627 }
    628 
    629 /*
    630 ================
    631 idRenderModelStatic::LoadModel
    632 ================
    633 */
    634 void idRenderModelStatic::LoadModel() {
    635 	PurgeModel();
    636 	InitFromFile( name );
    637 }
    638 
    639 /*
    640 ================
    641 idRenderModelStatic::InitEmpty
    642 ================
    643 */
    644 void idRenderModelStatic::InitEmpty( const char *fileName ) {
    645 	// model names of the form _area* are static parts of the
    646 	// world, and have already been considered for optimized shadows
    647 	// other model names are inline entity models, and need to be
    648 	// shadowed normally
    649 	if ( !idStr::Cmpn( fileName, "_area", 5 ) ) {
    650 		isStaticWorldModel = true;
    651 	} else {
    652 		isStaticWorldModel = false;
    653 	}
    654 
    655 	name = fileName;
    656 	reloadable = false;	// if it didn't come from a file, we can't reload it
    657 	PurgeModel();
    658 	purged = false;
    659 	bounds.Zero();
    660 }
    661 
    662 /*
    663 ================
    664 idRenderModelStatic::AddSurface
    665 ================
    666 */
    667 void idRenderModelStatic::AddSurface( modelSurface_t surface ) {
    668 	surfaces.Append( surface );
    669 	if ( surface.geometry ) {
    670 		bounds += surface.geometry->bounds;
    671 	}
    672 }
    673 
    674 /*
    675 ================
    676 idRenderModelStatic::Name
    677 ================
    678 */
    679 const char *idRenderModelStatic::Name() const {
    680 	return name;
    681 }
    682 
    683 /*
    684 ================
    685 idRenderModelStatic::Timestamp
    686 ================
    687 */
    688 ID_TIME_T idRenderModelStatic::Timestamp() const {
    689 	return timeStamp;
    690 }
    691 
    692 /*
    693 ================
    694 idRenderModelStatic::NumSurfaces
    695 ================
    696 */
    697 int idRenderModelStatic::NumSurfaces() const {
    698 	return surfaces.Num();
    699 }
    700 
    701 /*
    702 ================
    703 idRenderModelStatic::NumBaseSurfaces
    704 ================
    705 */
    706 int idRenderModelStatic::NumBaseSurfaces() const {
    707 	return surfaces.Num() - overlaysAdded;
    708 }
    709 
    710 /*
    711 ================
    712 idRenderModelStatic::Surface
    713 ================
    714 */
    715 const modelSurface_t *idRenderModelStatic::Surface( int surfaceNum ) const {
    716 	return &surfaces[surfaceNum];
    717 }
    718 
    719 /*
    720 ================
    721 idRenderModelStatic::AllocSurfaceTriangles
    722 ================
    723 */
    724 srfTriangles_t *idRenderModelStatic::AllocSurfaceTriangles( int numVerts, int numIndexes ) const {
    725 	srfTriangles_t *tri = R_AllocStaticTriSurf();
    726 	R_AllocStaticTriSurfVerts( tri, numVerts );
    727 	R_AllocStaticTriSurfIndexes( tri, numIndexes );
    728 	return tri;
    729 }
    730 
    731 /*
    732 ================
    733 idRenderModelStatic::FreeSurfaceTriangles
    734 ================
    735 */
    736 void idRenderModelStatic::FreeSurfaceTriangles( srfTriangles_t *tris ) const {
    737 	R_FreeStaticTriSurf( tris );
    738 }
    739 
    740 /*
    741 ================
    742 idRenderModelStatic::IsStaticWorldModel
    743 ================
    744 */
    745 bool idRenderModelStatic::IsStaticWorldModel() const {
    746 	return isStaticWorldModel;
    747 }
    748 
    749 /*
    750 ================
    751 idRenderModelStatic::IsDynamicModel
    752 ================
    753 */
    754 dynamicModel_t idRenderModelStatic::IsDynamicModel() const {
    755 	// dynamic subclasses will override this
    756 	return DM_STATIC;
    757 }
    758 
    759 /*
    760 ================
    761 idRenderModelStatic::IsReloadable
    762 ================
    763 */
    764 bool idRenderModelStatic::IsReloadable() const {
    765 	return reloadable;
    766 }
    767 
    768 /*
    769 ================
    770 idRenderModelStatic::Bounds
    771 ================
    772 */
    773 idBounds idRenderModelStatic::Bounds( const struct renderEntity_s *mdef ) const {
    774 	return bounds;
    775 }
    776 
    777 /*
    778 ================
    779 idRenderModelStatic::DepthHack
    780 ================
    781 */
    782 float idRenderModelStatic::DepthHack() const {
    783 	return 0.0f;
    784 }
    785 
    786 /*
    787 ================
    788 idRenderModelStatic::InstantiateDynamicModel
    789 ================
    790 */
    791 idRenderModel *idRenderModelStatic::InstantiateDynamicModel( const struct renderEntity_s *ent, const viewDef_t *view, idRenderModel *cachedModel ) {
    792 	if ( cachedModel ) {
    793 		delete cachedModel;
    794 		cachedModel = NULL;
    795 	}
    796 	common->Error( "InstantiateDynamicModel called on static model '%s'", name.c_str() );
    797 	return NULL;
    798 }
    799 
    800 /*
    801 ================
    802 idRenderModelStatic::NumJoints
    803 ================
    804 */
    805 int idRenderModelStatic::NumJoints() const {
    806 	return 0;
    807 }
    808 
    809 /*
    810 ================
    811 idRenderModelStatic::GetJoints
    812 ================
    813 */
    814 const idMD5Joint *idRenderModelStatic::GetJoints() const {
    815 	return NULL;
    816 }
    817 
    818 /*
    819 ================
    820 idRenderModelStatic::GetJointHandle
    821 ================
    822 */
    823 jointHandle_t idRenderModelStatic::GetJointHandle( const char *name ) const {
    824 	return INVALID_JOINT;
    825 }
    826 
    827 /*
    828 ================
    829 idRenderModelStatic::GetJointName
    830 ================
    831 */
    832 const char * idRenderModelStatic::GetJointName( jointHandle_t handle ) const {
    833 	return "";
    834 }
    835 
    836 /*
    837 ================
    838 idRenderModelStatic::GetDefaultPose
    839 ================
    840 */
    841 const idJointQuat *idRenderModelStatic::GetDefaultPose() const {
    842 	return NULL;
    843 }
    844 
    845 /*
    846 ================
    847 idRenderModelStatic::NearestJoint
    848 ================
    849 */
    850 int idRenderModelStatic::NearestJoint( int surfaceNum, int a, int b, int c ) const {
    851 	return INVALID_JOINT;
    852 }
    853 
    854 
    855 //=====================================================================
    856 
    857 
    858 /*
    859 ================
    860 idRenderModelStatic::FinishSurfaces
    861 
    862 The mergeShadows option allows surfaces with different textures to share
    863 silhouette edges for shadow calculation, instead of leaving shared edges
    864 hanging.
    865 
    866 If any of the original shaders have the noSelfShadow flag set, the surfaces
    867 can't be merged, because they will need to be drawn in different order.
    868 
    869 If there is only one surface, a separate merged surface won't be generated.
    870 
    871 A model with multiple surfaces can't later have a skinned shader change the
    872 state of the noSelfShadow flag.
    873 
    874 -----------------
    875 
    876 Creates mirrored copies of two sided surfaces with normal maps, which would
    877 otherwise light funny.
    878 
    879 Extends the bounds of deformed surfaces so they don't cull incorrectly at screen edges.
    880 
    881 ================
    882 */
    883 void idRenderModelStatic::FinishSurfaces() {
    884 	int			i;
    885 	int			totalVerts, totalIndexes;
    886 
    887 	hasDrawingSurfaces = false;
    888 	hasInteractingSurfaces = false;
    889 	hasShadowCastingSurfaces = false;
    890 	purged = false;
    891 
    892 	// make sure we don't have a huge bounds even if we don't finish everything
    893 	bounds.Zero();
    894 
    895 
    896 	if ( surfaces.Num() == 0 ) {
    897 		return;
    898 	}
    899 
    900 	// renderBump doesn't care about most of this
    901 	if ( fastLoad ) {
    902 		bounds.Zero();
    903 		for ( i = 0; i < surfaces.Num(); i++ ) {
    904 			const modelSurface_t	*surf = &surfaces[i];
    905 
    906 			R_BoundTriSurf( surf->geometry );
    907 			bounds.AddBounds( surf->geometry->bounds );
    908 		}
    909 
    910 		return;
    911 	}
    912 
    913 	// cleanup all the final surfaces, but don't create sil edges
    914 	totalVerts = 0;
    915 	totalIndexes = 0;
    916 
    917 	// decide if we are going to merge all the surfaces into one shadower
    918 	int	numOriginalSurfaces = surfaces.Num();
    919 
    920 	// make sure there aren't any NULL shaders or geometry
    921 	for ( i = 0; i < numOriginalSurfaces; i++ ) {
    922 		const modelSurface_t	*surf = &surfaces[i];
    923 
    924 		if ( surf->geometry == NULL || surf->shader == NULL ) {
    925 			MakeDefaultModel();
    926 			common->Error( "Model %s, surface %i had NULL geometry", name.c_str(), i );
    927 		}
    928 		if ( surf->shader == NULL ) {
    929 			MakeDefaultModel();
    930 			common->Error( "Model %s, surface %i had NULL shader", name.c_str(), i );
    931 		}
    932 	}
    933 
    934 	// duplicate and reverse triangles for two sided bump mapped surfaces
    935 	// note that this won't catch surfaces that have their shaders dynamically
    936 	// changed, and won't work with animated models.
    937 	// It is better to create completely separate surfaces, rather than
    938 	// add vertexes and indexes to the existing surface, because the
    939 	// tangent generation wouldn't like the acute shared edges
    940 	for ( i = 0; i < numOriginalSurfaces; i++ ) {
    941 		const modelSurface_t	*surf = &surfaces[i];
    942 
    943 		if ( surf->shader->ShouldCreateBackSides() ) {
    944 			srfTriangles_t *newTri;
    945 
    946 			newTri = R_CopyStaticTriSurf( surf->geometry );
    947 			R_ReverseTriangles( newTri );
    948 
    949 			modelSurface_t	newSurf;
    950 
    951 			newSurf.shader = surf->shader;
    952 			newSurf.geometry = newTri;
    953 
    954 			AddSurface( newSurf );
    955 		}
    956 	}
    957 
    958 	// clean the surfaces
    959 	for ( i = 0; i < surfaces.Num(); i++ ) {
    960 		const modelSurface_t	*surf = &surfaces[i];
    961 
    962 		R_CleanupTriangles( surf->geometry, surf->geometry->generateNormals, true, surf->shader->UseUnsmoothedTangents() );
    963 		if ( surf->shader->SurfaceCastsShadow() ) {
    964 			totalVerts += surf->geometry->numVerts;
    965 			totalIndexes += surf->geometry->numIndexes;
    966 		}
    967 	}
    968 
    969 	// add up the total surface area for development information
    970 	for ( i = 0; i < surfaces.Num(); i++ ) {
    971 		const modelSurface_t	*surf = &surfaces[i];
    972 		srfTriangles_t	*tri = surf->geometry;
    973 
    974 		for ( int j = 0; j < tri->numIndexes; j += 3 ) {
    975 			float	area = idWinding::TriangleArea( tri->verts[tri->indexes[j]].xyz,
    976 				 tri->verts[tri->indexes[j+1]].xyz,  tri->verts[tri->indexes[j+2]].xyz );
    977 			const_cast<idMaterial *>(surf->shader)->AddToSurfaceArea( area );
    978 		}
    979 	}
    980 
    981 	// set flags for whole-model rejection
    982 	for ( i = 0; i < surfaces.Num(); i++ ) {
    983 		const modelSurface_t	*surf = &surfaces[i];
    984 		if ( surf->shader->IsDrawn() ) {
    985 			hasDrawingSurfaces = true;
    986 		}
    987 		if ( surf->shader->SurfaceCastsShadow() ) {
    988 			hasShadowCastingSurfaces = true;
    989 		}
    990 		if ( surf->shader->ReceivesLighting() ) {
    991 			hasInteractingSurfaces = true;
    992 		}
    993 		if ( strstr( surf->shader->GetName(), "trigger" ) ) {
    994 			static int breakHere;
    995 			breakHere++;
    996 		}
    997 	}
    998 
    999 	// calculate the bounds
   1000 	if ( surfaces.Num() == 0 ) {
   1001 		bounds.Zero();
   1002 	} else {
   1003 		bounds.Clear();
   1004 		for ( i = 0; i < surfaces.Num(); i++ ) {
   1005 			modelSurface_t	*surf = &surfaces[i];
   1006 
   1007 			// if the surface has a deformation, increase the bounds
   1008 			// the amount here is somewhat arbitrary, designed to handle
   1009 			// autosprites and flares, but could be done better with exact
   1010 			// deformation information.
   1011 			// Note that this doesn't handle deformations that are skinned in
   1012 			// at run time...
   1013 			if ( surf->shader->Deform() != DFRM_NONE ) {
   1014 				srfTriangles_t	*tri = surf->geometry;
   1015 				idVec3	mid = ( tri->bounds[1] + tri->bounds[0] ) * 0.5f;
   1016 				float	radius = ( tri->bounds[0] - mid ).Length();
   1017 				radius += 20.0f;
   1018 
   1019 				tri->bounds[0][0] = mid[0] - radius;
   1020 				tri->bounds[0][1] = mid[1] - radius;
   1021 				tri->bounds[0][2] = mid[2] - radius;
   1022 
   1023 				tri->bounds[1][0] = mid[0] + radius;
   1024 				tri->bounds[1][1] = mid[1] + radius;
   1025 				tri->bounds[1][2] = mid[2] + radius;
   1026 			}
   1027 
   1028 			// add to the model bounds
   1029 			bounds.AddBounds( surf->geometry->bounds );
   1030 
   1031 		}
   1032 	}
   1033 }
   1034 
   1035 /*
   1036 =================
   1037 idRenderModelStatic::ConvertASEToModelSurfaces
   1038 =================
   1039 */
   1040 typedef struct matchVert_s {
   1041 	struct matchVert_s	*next;
   1042 	int		v, tv;
   1043 	byte	color[4];
   1044 	idVec3	normal;
   1045 } matchVert_t;
   1046 
   1047 bool idRenderModelStatic::ConvertASEToModelSurfaces( const struct aseModel_s *ase ) {
   1048 	aseObject_t *	object;
   1049 	aseMesh_t *		mesh;
   1050 	aseMaterial_t *	material;
   1051 	const idMaterial *im1, *im2;
   1052 	srfTriangles_t *tri;
   1053 	int				objectNum;
   1054 	int				i, j, k;
   1055 	int				v, tv;
   1056 	int *			vRemap;
   1057 	int *			tvRemap;
   1058 	matchVert_t *	mvTable;	// all of the match verts
   1059 	matchVert_t **	mvHash;		// points inside mvTable for each xyz index
   1060 	matchVert_t *	lastmv;
   1061 	matchVert_t *	mv;
   1062 	idVec3			normal;
   1063 	float			uOffset, vOffset, textureSin, textureCos;
   1064 	float			uTiling, vTiling;
   1065 	int *			mergeTo;
   1066 	byte *			color;
   1067 	static byte	identityColor[4] = { 255, 255, 255, 255 };
   1068 	modelSurface_t	surf, *modelSurf;
   1069 
   1070 	if ( !ase ) {
   1071 		return false;
   1072 	}
   1073 	if ( ase->objects.Num() < 1 ) {
   1074 		return false;
   1075 	}
   1076 
   1077 	timeStamp = ase->timeStamp;
   1078 
   1079 	// the modeling programs can save out multiple surfaces with a common
   1080 	// material, but we would like to mege them together where possible
   1081 	// meaning that this->NumSurfaces() <= ase->objects.currentElements
   1082 	mergeTo = (int *)_alloca( ase->objects.Num() * sizeof( *mergeTo ) ); 
   1083 	surf.geometry = NULL;
   1084 	if ( ase->materials.Num() == 0 ) {
   1085 		// if we don't have any materials, dump everything into a single surface
   1086 		surf.shader = tr.defaultMaterial;
   1087 		surf.id = 0;
   1088 		this->AddSurface( surf );
   1089 		for ( i = 0; i < ase->objects.Num(); i++ ) { 
   1090 			mergeTo[i] = 0;
   1091 		}
   1092 	} else if ( !r_mergeModelSurfaces.GetBool() ) {
   1093 		// don't merge any
   1094 		for ( i = 0; i < ase->objects.Num(); i++ ) { 
   1095 			mergeTo[i] = i;
   1096 			object = ase->objects[i];
   1097 			material = ase->materials[object->materialRef];
   1098 			surf.shader = declManager->FindMaterial( material->name );
   1099 			surf.id = this->NumSurfaces();
   1100 			this->AddSurface( surf );
   1101 		}
   1102 	} else {
   1103 		// search for material matches
   1104 		for ( i = 0; i < ase->objects.Num(); i++ ) { 
   1105 			object = ase->objects[i];
   1106 			material = ase->materials[object->materialRef];
   1107 			im1 = declManager->FindMaterial( material->name );
   1108 			if ( im1->IsDiscrete() ) {
   1109 				// flares, autosprites, etc
   1110 				j = this->NumSurfaces();
   1111 			} else {
   1112 				for ( j = 0; j < this->NumSurfaces(); j++ ) {
   1113 					modelSurf = &this->surfaces[j];
   1114 					im2 = modelSurf->shader;
   1115 					if ( im1 == im2 ) {
   1116 						// merge this
   1117 						mergeTo[i] = j;
   1118 						break;
   1119 					}
   1120 				}
   1121 			}
   1122 			if ( j == this->NumSurfaces() ) {
   1123 				// didn't merge
   1124 				mergeTo[i] = j;
   1125 				surf.shader = im1;
   1126 				surf.id = this->NumSurfaces();
   1127 				this->AddSurface( surf );
   1128 			}
   1129 		}
   1130 	}
   1131 
   1132 	idVectorSubset<idVec3, 3> vertexSubset;
   1133 	idVectorSubset<idVec2, 2> texCoordSubset;
   1134 
   1135 	// build the surfaces
   1136 	for ( objectNum = 0; objectNum < ase->objects.Num(); objectNum++ ) {
   1137 		object = ase->objects[objectNum];
   1138 		mesh = &object->mesh;
   1139 		material = ase->materials[object->materialRef];
   1140 		im1 = declManager->FindMaterial( material->name );
   1141 
   1142 		bool normalsParsed = mesh->normalsParsed;
   1143 
   1144 		// completely ignore any explict normals on surfaces with a renderbump command
   1145 		// which will guarantee the best contours and least vertexes.
   1146 		const char *rb = im1->GetRenderBump();
   1147 		if ( rb != NULL && rb[0] != NULL ) {
   1148 			normalsParsed = false;
   1149 		}
   1150 
   1151 		// It seems like the tools our artists are using often generate
   1152 		// verts and texcoords slightly separated that should be merged
   1153 		// note that we really should combine the surfaces with common materials
   1154 		// before doing this operation, because we can miss a slop combination
   1155 		// if they are in different surfaces
   1156 
   1157 		vRemap = (int *)R_StaticAlloc( mesh->numVertexes * sizeof( vRemap[0] ), TAG_MODEL );
   1158 
   1159 		if ( fastLoad ) {
   1160 			// renderbump doesn't care about vertex count
   1161 			for ( j = 0; j < mesh->numVertexes; j++ ) {
   1162 				vRemap[j] = j;
   1163 			}
   1164 		} else {
   1165 			float vertexEpsilon = r_slopVertex.GetFloat();
   1166 			float expand = 2 * 32 * vertexEpsilon;
   1167 			idVec3 mins, maxs;
   1168 
   1169 			SIMDProcessor->MinMax( mins, maxs, mesh->vertexes, mesh->numVertexes );
   1170 			mins -= idVec3( expand, expand, expand );
   1171 			maxs += idVec3( expand, expand, expand );
   1172 			vertexSubset.Init( mins, maxs, 32, 1024 );
   1173 			for ( j = 0; j < mesh->numVertexes; j++ ) {
   1174 				vRemap[j] = vertexSubset.FindVector( mesh->vertexes, j, vertexEpsilon );
   1175 			}
   1176 		}
   1177 
   1178 		tvRemap = (int *)R_StaticAlloc( mesh->numTVertexes * sizeof( tvRemap[0] ), TAG_MODEL );
   1179 
   1180 		if ( fastLoad ) {
   1181 			// renderbump doesn't care about vertex count
   1182 			for ( j = 0; j < mesh->numTVertexes; j++ ) {
   1183 				tvRemap[j] = j;
   1184 			}
   1185 		} else {
   1186 			float texCoordEpsilon = r_slopTexCoord.GetFloat();
   1187 			float expand = 2 * 32 * texCoordEpsilon;
   1188 			idVec2 mins, maxs;
   1189 
   1190 			SIMDProcessor->MinMax( mins, maxs, mesh->tvertexes, mesh->numTVertexes );
   1191 			mins -= idVec2( expand, expand );
   1192 			maxs += idVec2( expand, expand );
   1193 			texCoordSubset.Init( mins, maxs, 32, 1024 );
   1194 			for ( j = 0; j < mesh->numTVertexes; j++ ) {
   1195 				tvRemap[j] = texCoordSubset.FindVector( mesh->tvertexes, j, texCoordEpsilon );
   1196 			}
   1197 		}
   1198 
   1199 		// we need to find out how many unique vertex / texcoord combinations
   1200 		// there are, because ASE tracks them separately but we need them unified
   1201 
   1202 		// the maximum possible number of combined vertexes is the number of indexes
   1203 		mvTable = (matchVert_t *)R_ClearedStaticAlloc( mesh->numFaces * 3 * sizeof( mvTable[0] ) );
   1204 
   1205 		// we will have a hash chain based on the xyz values
   1206 		mvHash = (matchVert_t **)R_ClearedStaticAlloc( mesh->numVertexes * sizeof( mvHash[0] ) );
   1207 
   1208 		// allocate triangle surface
   1209 		tri = R_AllocStaticTriSurf();
   1210 		tri->numVerts = 0;
   1211 		tri->numIndexes = 0;
   1212 		R_AllocStaticTriSurfIndexes( tri, mesh->numFaces * 3 );
   1213 		tri->generateNormals = !normalsParsed;
   1214 
   1215 		// init default normal, color and tex coord index
   1216 		normal.Zero();
   1217 		color = identityColor;
   1218 		tv = 0;
   1219 
   1220 		// find all the unique combinations
   1221 		float normalEpsilon = 1.0f - r_slopNormal.GetFloat();
   1222 		for ( j = 0; j < mesh->numFaces; j++ ) {
   1223 			for ( k = 0; k < 3; k++ ) {
   1224 				v = mesh->faces[j].vertexNum[k];
   1225 
   1226 				if ( v < 0 || v >= mesh->numVertexes ) {
   1227 					common->Error( "ConvertASEToModelSurfaces: bad vertex index in ASE file %s", name.c_str() );
   1228 				}
   1229 
   1230 				// collapse the position if it was slightly offset 
   1231 				v = vRemap[v];
   1232 
   1233 				// we may or may not have texcoords to compare
   1234 				if ( mesh->numTVFaces == mesh->numFaces && mesh->numTVertexes != 0 ) {
   1235 					tv = mesh->faces[j].tVertexNum[k];
   1236 					if ( tv < 0 || tv >= mesh->numTVertexes ) {
   1237 						common->Error( "ConvertASEToModelSurfaces: bad tex coord index in ASE file %s", name.c_str() );
   1238 					}
   1239 					// collapse the tex coord if it was slightly offset
   1240 					tv = tvRemap[tv];
   1241 				}
   1242 
   1243 				// we may or may not have normals to compare
   1244 				if ( normalsParsed ) {
   1245 					normal = mesh->faces[j].vertexNormals[k];
   1246 				}
   1247 
   1248 				// we may or may not have colors to compare
   1249 				if ( mesh->colorsParsed ) {
   1250 					color = mesh->faces[j].vertexColors[k];
   1251 				}
   1252 
   1253 				// find a matching vert
   1254 				for ( lastmv = NULL, mv = mvHash[v]; mv != NULL; lastmv = mv, mv = mv->next ) {
   1255 					if ( mv->tv != tv ) {
   1256 						continue;
   1257 					}
   1258 					if ( *(unsigned *)mv->color != *(unsigned *)color ) {
   1259 						continue;
   1260 					}
   1261 					if ( !normalsParsed ) {
   1262 						// if we are going to create the normals, just
   1263 						// matching texcoords is enough
   1264 						break;
   1265 					}
   1266 					if ( mv->normal * normal > normalEpsilon ) {
   1267 						break;		// we already have this one
   1268 					}
   1269 				}
   1270 				if ( !mv ) {
   1271 					// allocate a new match vert and link to hash chain
   1272 					mv = &mvTable[ tri->numVerts ];
   1273 					mv->v = v;
   1274 					mv->tv = tv;
   1275 					mv->normal = normal;
   1276 					*(unsigned *)mv->color = *(unsigned *)color;
   1277 					mv->next = NULL;
   1278 					if ( lastmv ) {
   1279 						lastmv->next = mv;
   1280 					} else {
   1281 						mvHash[v] = mv;
   1282 					}
   1283 					tri->numVerts++;
   1284 				}
   1285 
   1286 				tri->indexes[tri->numIndexes] = mv - mvTable;
   1287 				tri->numIndexes++;
   1288 			}
   1289 		}
   1290 
   1291 		// allocate space for the indexes and copy them
   1292 		if ( tri->numIndexes > mesh->numFaces * 3 ) {
   1293 			common->FatalError( "ConvertASEToModelSurfaces: index miscount in ASE file %s", name.c_str() );
   1294 		}
   1295 		if ( tri->numVerts > mesh->numFaces * 3 ) {
   1296 			common->FatalError( "ConvertASEToModelSurfaces: vertex miscount in ASE file %s", name.c_str() );
   1297 		}
   1298 
   1299 		// an ASE allows the texture coordinates to be scaled, translated, and rotated
   1300 		if ( ase->materials.Num() == 0 ) {
   1301 			uOffset = vOffset = 0.0f;
   1302 			uTiling = vTiling = 1.0f;
   1303 			textureSin = 0.0f;
   1304 			textureCos = 1.0f;
   1305 		} else {
   1306 			material = ase->materials[object->materialRef];
   1307 			uOffset = -material->uOffset;
   1308 			vOffset = material->vOffset;
   1309 			uTiling = material->uTiling;
   1310 			vTiling = material->vTiling;
   1311 			textureSin = idMath::Sin( material->angle );
   1312 			textureCos = idMath::Cos( material->angle );
   1313 		}
   1314 
   1315 		// now allocate and generate the combined vertexes
   1316 		R_AllocStaticTriSurfVerts( tri, tri->numVerts );
   1317 
   1318 		for ( j = 0; j < tri->numVerts; j++ ) {
   1319 			mv = &mvTable[j];
   1320 			tri->verts[ j ].Clear();
   1321 			tri->verts[ j ].xyz = mesh->vertexes[ mv->v ];
   1322 			tri->verts[ j ].SetNormal( mv->normal );
   1323 			*(unsigned *)tri->verts[j].color = *(unsigned *)mv->color;
   1324 			if ( mesh->numTVFaces == mesh->numFaces && mesh->numTVertexes != 0 ) {
   1325 				const idVec2 &tv = mesh->tvertexes[ mv->tv ];
   1326 				float u = tv.x * uTiling + uOffset;
   1327 				float v = tv.y * vTiling + vOffset;
   1328 				tri->verts[j].SetTexCoord( u * textureCos + v * textureSin, u * -textureSin + v * textureCos );
   1329 			}
   1330 		}
   1331 
   1332 		R_StaticFree( mvTable );
   1333 		R_StaticFree( mvHash );
   1334 		R_StaticFree( tvRemap );
   1335 		R_StaticFree( vRemap );
   1336 
   1337 		// see if we need to merge with a previous surface of the same material
   1338 		modelSurf = &this->surfaces[mergeTo[ objectNum ]];
   1339 		srfTriangles_t	*mergeTri = modelSurf->geometry;
   1340 		if ( !mergeTri ) {
   1341 			modelSurf->geometry = tri;
   1342 		} else {
   1343 			modelSurf->geometry = R_MergeTriangles( mergeTri, tri );
   1344 			R_FreeStaticTriSurf( tri );
   1345 			R_FreeStaticTriSurf( mergeTri );
   1346 		}
   1347 	}
   1348 
   1349 	return true;
   1350 }
   1351 
   1352 /*
   1353 =================
   1354 idRenderModelStatic::ConvertLWOToModelSurfaces
   1355 =================
   1356 */
   1357 bool idRenderModelStatic::ConvertLWOToModelSurfaces( const struct st_lwObject *lwo ) {
   1358 	const idMaterial *im1, *im2;
   1359 	srfTriangles_t	*tri;
   1360 	lwSurface *		lwoSurf;
   1361 	int				numTVertexes;
   1362 	int				i, j, k;
   1363 	int				v, tv;
   1364 	idVec3 *		vList;
   1365 	int *			vRemap;
   1366 	idVec2 *		tvList;
   1367 	int *			tvRemap;
   1368 	matchVert_t *	mvTable;	// all of the match verts
   1369 	matchVert_t **	mvHash;		// points inside mvTable for each xyz index
   1370 	matchVert_t *	lastmv;
   1371 	matchVert_t *	mv;
   1372 	idVec3			normal;
   1373 	int *			mergeTo;
   1374 	byte			color[4];
   1375 	modelSurface_t	surf, *modelSurf;
   1376 
   1377 	if ( !lwo ) {
   1378 		return false;
   1379 	}
   1380 	if ( lwo->surf == NULL ) {
   1381 		return false;
   1382 	}
   1383 
   1384 	timeStamp = lwo->timeStamp;
   1385 
   1386 	// count the number of surfaces
   1387 	i = 0;
   1388 	for ( lwoSurf = lwo->surf; lwoSurf; lwoSurf = lwoSurf->next ) {
   1389 		i++;
   1390 	}
   1391 
   1392 	// the modeling programs can save out multiple surfaces with a common
   1393 	// material, but we would like to merge them together where possible
   1394 	mergeTo = (int *)_alloca( i * sizeof( mergeTo[0] ) ); 
   1395 	memset( &surf, 0, sizeof( surf ) );
   1396 
   1397 	if ( !r_mergeModelSurfaces.GetBool() ) {
   1398 		// don't merge any
   1399 		for ( lwoSurf = lwo->surf, i = 0; lwoSurf; lwoSurf = lwoSurf->next, i++ ) {
   1400 			mergeTo[i] = i;
   1401 			surf.shader = declManager->FindMaterial( lwoSurf->name );
   1402 			surf.id = this->NumSurfaces();
   1403 			this->AddSurface( surf );
   1404 		}
   1405 	} else {
   1406 		// search for material matches
   1407 		for ( lwoSurf = lwo->surf, i = 0; lwoSurf; lwoSurf = lwoSurf->next, i++ ) {
   1408 			im1 = declManager->FindMaterial( lwoSurf->name );
   1409 			if ( im1->IsDiscrete() ) {
   1410 				// flares, autosprites, etc
   1411 				j = this->NumSurfaces();
   1412 			} else {
   1413 				for ( j = 0; j < this->NumSurfaces(); j++ ) {
   1414 					modelSurf = &this->surfaces[j];
   1415 					im2 = modelSurf->shader;
   1416 					if ( im1 == im2 ) {
   1417 						// merge this
   1418 						mergeTo[i] = j;
   1419 						break;
   1420 					}
   1421 				}
   1422 			}
   1423 			if ( j == this->NumSurfaces() ) {
   1424 				// didn't merge
   1425 				mergeTo[i] = j;
   1426 				surf.shader = im1;
   1427 				surf.id = this->NumSurfaces();
   1428 				this->AddSurface( surf );
   1429 			}
   1430 		}
   1431 	}
   1432 
   1433 	idVectorSubset<idVec3, 3> vertexSubset;
   1434 	idVectorSubset<idVec2, 2> texCoordSubset;
   1435 
   1436 	// we only ever use the first layer
   1437 	lwLayer *layer = lwo->layer;
   1438 
   1439 	// vertex positions
   1440 	if ( layer->point.count <= 0 ) {
   1441 		common->Warning( "ConvertLWOToModelSurfaces: model \'%s\' has bad or missing vertex data", name.c_str() );
   1442 		return false;
   1443 	}
   1444 
   1445 	vList = (idVec3 *)R_StaticAlloc( layer->point.count * sizeof( vList[0] ), TAG_MODEL );
   1446 	for ( j = 0; j < layer->point.count; j++ ) {
   1447 		vList[j].x = layer->point.pt[j].pos[0];
   1448 		vList[j].y = layer->point.pt[j].pos[2];
   1449 		vList[j].z = layer->point.pt[j].pos[1];
   1450 	}
   1451 
   1452 	// vertex texture coords
   1453 	numTVertexes = 0;
   1454 
   1455 	if ( layer->nvmaps ) {
   1456 		for( lwVMap *vm = layer->vmap; vm; vm = vm->next ) {
   1457 			if ( vm->type == LWID_('T','X','U','V') ) {
   1458 				numTVertexes += vm->nverts;
   1459 			}
   1460 		}
   1461 	}
   1462 
   1463 	if ( numTVertexes ) {
   1464 		tvList = (idVec2 *)Mem_Alloc( numTVertexes * sizeof( tvList[0] ), TAG_MODEL );
   1465 		int offset = 0;
   1466 		for( lwVMap *vm = layer->vmap; vm; vm = vm->next ) {
   1467 			if ( vm->type == LWID_('T','X','U','V') ) {
   1468 	  			vm->offset = offset;
   1469 		  		for ( k = 0; k < vm->nverts; k++ ) {
   1470 		  		   	tvList[k + offset].x = vm->val[k][0];
   1471 					tvList[k + offset].y = 1.0f - vm->val[k][1];	// invert the t
   1472 		  		}
   1473 			  	offset += vm->nverts;
   1474 			}
   1475 		}
   1476 	} else {
   1477 		common->Warning( "ConvertLWOToModelSurfaces: model \'%s\' has bad or missing uv data", name.c_str() );
   1478 	  	numTVertexes = 1;
   1479 		tvList = (idVec2 *)Mem_ClearedAlloc( numTVertexes * sizeof( tvList[0] ), TAG_MODEL );
   1480 	}
   1481 
   1482 	// It seems like the tools our artists are using often generate
   1483 	// verts and texcoords slightly separated that should be merged
   1484 	// note that we really should combine the surfaces with common materials
   1485 	// before doing this operation, because we can miss a slop combination
   1486 	// if they are in different surfaces
   1487 
   1488 	vRemap = (int *)R_StaticAlloc( layer->point.count * sizeof( vRemap[0] ), TAG_MODEL );
   1489 
   1490 	if ( fastLoad ) {
   1491 		// renderbump doesn't care about vertex count
   1492 		for ( j = 0; j < layer->point.count; j++ ) {
   1493 			vRemap[j] = j;
   1494 		}
   1495 	} else {
   1496 		float vertexEpsilon = r_slopVertex.GetFloat();
   1497 		float expand = 2 * 32 * vertexEpsilon;
   1498 		idVec3 mins, maxs;
   1499 
   1500 		SIMDProcessor->MinMax( mins, maxs, vList, layer->point.count );
   1501 		mins -= idVec3( expand, expand, expand );
   1502 		maxs += idVec3( expand, expand, expand );
   1503 		vertexSubset.Init( mins, maxs, 32, 1024 );
   1504 		for ( j = 0; j < layer->point.count; j++ ) {
   1505 			vRemap[j] = vertexSubset.FindVector( vList, j, vertexEpsilon );
   1506 		}
   1507 	}
   1508 
   1509 	tvRemap = (int *)R_StaticAlloc( numTVertexes * sizeof( tvRemap[0] ), TAG_MODEL );
   1510 
   1511 	if ( fastLoad ) {
   1512 		// renderbump doesn't care about vertex count
   1513 		for ( j = 0; j < numTVertexes; j++ ) {
   1514 			tvRemap[j] = j;
   1515 		}
   1516 	} else {
   1517 		float texCoordEpsilon = r_slopTexCoord.GetFloat();
   1518 		float expand = 2 * 32 * texCoordEpsilon;
   1519 		idVec2 mins, maxs;
   1520 
   1521 		SIMDProcessor->MinMax( mins, maxs, tvList, numTVertexes );
   1522 		mins -= idVec2( expand, expand );
   1523 		maxs += idVec2( expand, expand );
   1524 		texCoordSubset.Init( mins, maxs, 32, 1024 );
   1525 		for ( j = 0; j < numTVertexes; j++ ) {
   1526 			tvRemap[j] = texCoordSubset.FindVector( tvList, j, texCoordEpsilon );
   1527 		}
   1528 	}
   1529 
   1530 	// build the surfaces
   1531 	for ( lwoSurf = lwo->surf, i = 0; lwoSurf; lwoSurf = lwoSurf->next, i++ ) {
   1532 		im1 = declManager->FindMaterial( lwoSurf->name );
   1533 
   1534 		bool normalsParsed = true;
   1535 
   1536 		// completely ignore any explict normals on surfaces with a renderbump command
   1537 		// which will guarantee the best contours and least vertexes.
   1538 		const char *rb = im1->GetRenderBump();
   1539 		if ( rb && rb[0] ) {
   1540 			normalsParsed = false;
   1541 		}
   1542 
   1543 		// we need to find out how many unique vertex / texcoord combinations there are
   1544 
   1545 		// the maximum possible number of combined vertexes is the number of indexes
   1546 		mvTable = (matchVert_t *)R_ClearedStaticAlloc( layer->polygon.count * 3 * sizeof( mvTable[0] ) );
   1547 
   1548 		// we will have a hash chain based on the xyz values
   1549 		mvHash = (matchVert_t **)R_ClearedStaticAlloc( layer->point.count * sizeof( mvHash[0] ) );
   1550 
   1551 		// allocate triangle surface
   1552 		tri = R_AllocStaticTriSurf();
   1553 		tri->numVerts = 0;
   1554 		tri->numIndexes = 0;
   1555 		R_AllocStaticTriSurfIndexes( tri, layer->polygon.count * 3 );
   1556 		tri->generateNormals = !normalsParsed;
   1557 
   1558 		// find all the unique combinations
   1559 		float	normalEpsilon;
   1560 		if ( fastLoad ) {
   1561 			normalEpsilon = 1.0f;	// don't merge unless completely exact
   1562 		} else {
   1563 			normalEpsilon = 1.0f - r_slopNormal.GetFloat();
   1564 		}
   1565 		for ( j = 0; j < layer->polygon.count; j++ ) {
   1566 			lwPolygon *poly = &layer->polygon.pol[j];
   1567 
   1568 			if ( poly->surf != lwoSurf ) {
   1569 				continue;
   1570 			}
   1571 
   1572 			if ( poly->nverts != 3 ) {
   1573 				common->Warning( "ConvertLWOToModelSurfaces: model %s has too many verts for a poly! Make sure you triplet it down", name.c_str() );
   1574 				continue;
   1575 			}
   1576 
   1577 			for ( k = 0; k < 3; k++ ) {
   1578 
   1579 				v = vRemap[poly->v[k].index];
   1580 
   1581 				normal.x = poly->v[k].norm[0];
   1582 				normal.y = poly->v[k].norm[2];
   1583 				normal.z = poly->v[k].norm[1];
   1584 
   1585 				// LWO models aren't all that pretty when it comes down to the floating point values they store
   1586 				normal.FixDegenerateNormal();
   1587 
   1588 				tv = 0;
   1589 
   1590 				color[0] = lwoSurf->color.rgb[0] * 255;
   1591 				color[1] = lwoSurf->color.rgb[1] * 255;
   1592 				color[2] = lwoSurf->color.rgb[2] * 255;
   1593 				color[3] = 255;
   1594 
   1595 				// first set attributes from the vertex
   1596 				lwPoint	*pt = &layer->point.pt[poly->v[k].index];
   1597 				int nvm;
   1598 				for ( nvm = 0; nvm < pt->nvmaps; nvm++ ) {
   1599 					lwVMapPt *vm = &pt->vm[nvm];
   1600 
   1601 					if ( vm->vmap->type == LWID_('T','X','U','V') ) {
   1602 						tv = tvRemap[vm->index + vm->vmap->offset];
   1603 					}
   1604 					if ( vm->vmap->type == LWID_('R','G','B','A') ) {
   1605 						for ( int chan = 0; chan < 4; chan++ ) {
   1606 							color[chan] = 255 * vm->vmap->val[vm->index][chan];
   1607 						}
   1608 					}
   1609 				}
   1610 
   1611 				// then override with polygon attributes
   1612 				for ( nvm = 0; nvm < poly->v[k].nvmaps; nvm++ ) {
   1613 					lwVMapPt *vm = &poly->v[k].vm[nvm];
   1614 
   1615 					if ( vm->vmap->type == LWID_('T','X','U','V') ) {
   1616 						tv = tvRemap[vm->index + vm->vmap->offset];
   1617 					}
   1618 					if ( vm->vmap->type == LWID_('R','G','B','A') ) {
   1619 						for ( int chan = 0; chan < 4; chan++ ) {
   1620 							color[chan] = 255 * vm->vmap->val[vm->index][chan];
   1621 						}
   1622 					}
   1623 				}
   1624 
   1625 				// find a matching vert
   1626 				for ( lastmv = NULL, mv = mvHash[v]; mv != NULL; lastmv = mv, mv = mv->next ) {
   1627 					if ( mv->tv != tv ) {
   1628 						continue;
   1629 					}
   1630 					if ( *(unsigned *)mv->color != *(unsigned *)color ) {
   1631 						continue;
   1632 					}
   1633 					if ( !normalsParsed ) {
   1634 						// if we are going to create the normals, just
   1635 						// matching texcoords is enough
   1636 						break;
   1637 					}
   1638 					if ( mv->normal * normal > normalEpsilon ) {
   1639 						break;		// we already have this one
   1640 					}
   1641 				}
   1642 				if ( !mv ) {
   1643 					// allocate a new match vert and link to hash chain
   1644 					mv = &mvTable[ tri->numVerts ];
   1645 					mv->v = v;
   1646 					mv->tv = tv;
   1647 					mv->normal = normal;
   1648 					*(unsigned *)mv->color = *(unsigned *)color;
   1649 					mv->next = NULL;
   1650 					if ( lastmv ) {
   1651 						lastmv->next = mv;
   1652 					} else {
   1653 						mvHash[v] = mv;
   1654 					}
   1655 					tri->numVerts++;
   1656 				}
   1657 
   1658 				tri->indexes[tri->numIndexes] = mv - mvTable;
   1659 				tri->numIndexes++;
   1660 			}
   1661 		}
   1662 
   1663 		// allocate space for the indexes and copy them
   1664 		if ( tri->numIndexes > layer->polygon.count * 3 ) {
   1665 			common->FatalError( "ConvertLWOToModelSurfaces: index miscount in LWO file %s", name.c_str() );
   1666 		}
   1667 		if ( tri->numVerts > layer->polygon.count * 3 ) {
   1668 			common->FatalError( "ConvertLWOToModelSurfaces: vertex miscount in LWO file %s", name.c_str() );
   1669 		}
   1670 
   1671 		// now allocate and generate the combined vertexes
   1672 		R_AllocStaticTriSurfVerts( tri, tri->numVerts );
   1673 
   1674 		for ( j = 0; j < tri->numVerts; j++ ) {
   1675 			mv = &mvTable[j];
   1676 			tri->verts[ j ].Clear();
   1677 			tri->verts[ j ].xyz = vList[ mv->v ];
   1678 			tri->verts[ j ].SetTexCoord( tvList[ mv->tv ] );
   1679 			tri->verts[ j ].SetNormal( mv->normal );
   1680 			*(unsigned *)tri->verts[j].color = *(unsigned *)mv->color;
   1681 		}
   1682 
   1683 		R_StaticFree( mvTable );
   1684 		R_StaticFree( mvHash );
   1685 
   1686 		// see if we need to merge with a previous surface of the same material
   1687 		modelSurf = &this->surfaces[mergeTo[ i ]];
   1688 		srfTriangles_t	*mergeTri = modelSurf->geometry;
   1689 		if ( !mergeTri ) {
   1690 			modelSurf->geometry = tri;
   1691 		} else {
   1692 			modelSurf->geometry = R_MergeTriangles( mergeTri, tri );
   1693 			R_FreeStaticTriSurf( tri );
   1694 			R_FreeStaticTriSurf( mergeTri );
   1695 		}
   1696 	}
   1697 
   1698 	R_StaticFree( tvRemap );
   1699 	R_StaticFree( vRemap );
   1700 	R_StaticFree( tvList );
   1701 	R_StaticFree( vList );
   1702 
   1703 	return true;
   1704 }
   1705 
   1706 /*
   1707 =================
   1708 idRenderModelStatic::ConvertLWOToASE
   1709 =================
   1710 */
   1711 struct aseModel_s *idRenderModelStatic::ConvertLWOToASE( const struct st_lwObject *obj, const char *fileName ) {
   1712 	int j, k;
   1713 	aseModel_t *ase;
   1714 
   1715 	if ( !obj ) {
   1716 		return NULL;
   1717 	}
   1718 
   1719 	// NOTE: using new operator because aseModel_t contains idList class objects
   1720 	ase = new (TAG_MODEL) aseModel_t;
   1721 	ase->timeStamp = obj->timeStamp;
   1722 	ase->objects.Resize( obj->nlayers, obj->nlayers );
   1723 
   1724 	int materialRef = 0;
   1725 
   1726 	for ( lwSurface *surf = obj->surf; surf; surf = surf->next ) {
   1727 
   1728 		aseMaterial_t *mat = (aseMaterial_t *)Mem_ClearedAlloc( sizeof( *mat ), TAG_MODEL );
   1729 		strcpy( mat->name, surf->name );
   1730 		mat->uTiling = mat->vTiling = 1;
   1731 		mat->angle = mat->uOffset = mat->vOffset = 0;
   1732 		ase->materials.Append( mat );
   1733 
   1734 		lwLayer *layer = obj->layer;
   1735 
   1736 		aseObject_t *object = (aseObject_t *)Mem_ClearedAlloc( sizeof( *object ), TAG_MODEL );
   1737 		object->materialRef = materialRef++;
   1738 
   1739 		aseMesh_t *mesh = &object->mesh;
   1740 		ase->objects.Append( object );
   1741 
   1742 		mesh->numFaces = layer->polygon.count;
   1743 		mesh->numTVFaces = mesh->numFaces;
   1744 		mesh->faces = (aseFace_t *)Mem_Alloc( mesh->numFaces  * sizeof( mesh->faces[0] ), TAG_MODEL );
   1745 
   1746 		mesh->numVertexes = layer->point.count;
   1747 		mesh->vertexes = (idVec3 *)Mem_Alloc( mesh->numVertexes * sizeof( mesh->vertexes[0] ), TAG_MODEL );
   1748 
   1749 		// vertex positions
   1750 		if ( layer->point.count <= 0 ) {
   1751 			common->Warning( "ConvertLWOToASE: model \'%s\' has bad or missing vertex data", name.c_str() );
   1752 		}
   1753 
   1754 		for ( j = 0; j < layer->point.count; j++ ) {
   1755 			mesh->vertexes[j].x = layer->point.pt[j].pos[0];
   1756 			mesh->vertexes[j].y = layer->point.pt[j].pos[2];
   1757 			mesh->vertexes[j].z = layer->point.pt[j].pos[1];
   1758 		}
   1759 
   1760 		// vertex texture coords
   1761 		mesh->numTVertexes = 0;
   1762 
   1763 		if ( layer->nvmaps ) {
   1764 		  	for( lwVMap *vm = layer->vmap; vm; vm = vm->next ) {
   1765 				if ( vm->type == LWID_('T','X','U','V') ) {
   1766 					mesh->numTVertexes += vm->nverts;
   1767 				}
   1768 			}
   1769 		}
   1770 
   1771 		if ( mesh->numTVertexes ) {
   1772 		  	mesh->tvertexes = (idVec2 *)Mem_Alloc( mesh->numTVertexes * sizeof( mesh->tvertexes[0] ), TAG_MODEL );
   1773 		  	int offset = 0;
   1774 		  	for( lwVMap *vm = layer->vmap; vm; vm = vm->next ) {
   1775 				if ( vm->type == LWID_('T','X','U','V') ) {
   1776 	  				vm->offset = offset;
   1777 		  			for ( k = 0; k < vm->nverts; k++ ) {
   1778 		  		   		mesh->tvertexes[k + offset].x = vm->val[k][0];
   1779 						mesh->tvertexes[k + offset].y = 1.0f - vm->val[k][1];	// invert the t
   1780 		  		   	}
   1781 			  		offset += vm->nverts;
   1782 				}
   1783 		  	}
   1784 	  	} else {
   1785 			common->Warning( "ConvertLWOToASE: model \'%s\' has bad or missing uv data", fileName );
   1786 	  		mesh->numTVertexes = 1;
   1787 	  		mesh->tvertexes = (idVec2 *)Mem_ClearedAlloc( mesh->numTVertexes * sizeof( mesh->tvertexes[0] ), TAG_MODEL );
   1788 	  	}
   1789 
   1790 		mesh->normalsParsed = true;
   1791 		mesh->colorsParsed = true;	// because we are falling back to the surface color
   1792 
   1793 		// triangles
   1794 		int faceIndex = 0;
   1795 		for ( j = 0; j < layer->polygon.count; j++ ) {
   1796 			lwPolygon *poly = &layer->polygon.pol[j];
   1797 
   1798 			if ( poly->surf != surf ) {
   1799 				continue;
   1800 			}
   1801 
   1802 			if ( poly->nverts != 3 ) {
   1803 				common->Warning( "ConvertLWOToASE: model %s has too many verts for a poly! Make sure you triplet it down", fileName );
   1804 				continue;
   1805 			}
   1806 	
   1807 			mesh->faces[faceIndex].faceNormal.x = poly->norm[0];
   1808 			mesh->faces[faceIndex].faceNormal.y = poly->norm[2];
   1809 			mesh->faces[faceIndex].faceNormal.z = poly->norm[1];
   1810 
   1811 			for ( k = 0; k < 3; k++ ) {
   1812 
   1813 				mesh->faces[faceIndex].vertexNum[k] = poly->v[k].index;
   1814 
   1815 				mesh->faces[faceIndex].vertexNormals[k].x = poly->v[k].norm[0];
   1816 				mesh->faces[faceIndex].vertexNormals[k].y = poly->v[k].norm[2];
   1817 				mesh->faces[faceIndex].vertexNormals[k].z = poly->v[k].norm[1];
   1818 
   1819 				// complete fallbacks
   1820 				mesh->faces[faceIndex].tVertexNum[k] = 0;
   1821 
   1822 				mesh->faces[faceIndex].vertexColors[k][0] = surf->color.rgb[0] * 255;
   1823 				mesh->faces[faceIndex].vertexColors[k][1] = surf->color.rgb[1] * 255;
   1824 				mesh->faces[faceIndex].vertexColors[k][2] = surf->color.rgb[2] * 255;
   1825 				mesh->faces[faceIndex].vertexColors[k][3] = 255;
   1826 
   1827 				// first set attributes from the vertex
   1828 				lwPoint	*pt = &layer->point.pt[poly->v[k].index];
   1829 				int nvm;
   1830 				for ( nvm = 0; nvm < pt->nvmaps; nvm++ ) {
   1831 					lwVMapPt *vm = &pt->vm[nvm];
   1832 
   1833 					if ( vm->vmap->type == LWID_('T','X','U','V') ) {
   1834 						mesh->faces[faceIndex].tVertexNum[k] = vm->index + vm->vmap->offset;
   1835 					}
   1836 					if ( vm->vmap->type == LWID_('R','G','B','A') ) {
   1837 						for ( int chan = 0; chan < 4; chan++ ) {
   1838 							mesh->faces[faceIndex].vertexColors[k][chan] = 255 * vm->vmap->val[vm->index][chan];
   1839 						}
   1840 					}
   1841 				}
   1842 
   1843 				// then override with polygon attributes
   1844 				for ( nvm = 0; nvm < poly->v[k].nvmaps; nvm++ ) {
   1845 					lwVMapPt *vm = &poly->v[k].vm[nvm];
   1846 
   1847 					if ( vm->vmap->type == LWID_('T','X','U','V') ) {
   1848 						mesh->faces[faceIndex].tVertexNum[k] = vm->index + vm->vmap->offset;
   1849 					}
   1850 					if ( vm->vmap->type == LWID_('R','G','B','A') ) {
   1851 						for ( int chan = 0; chan < 4; chan++ ) {
   1852 							mesh->faces[faceIndex].vertexColors[k][chan] = 255 * vm->vmap->val[vm->index][chan];
   1853 						}
   1854 					}
   1855 				}
   1856 			}
   1857 
   1858 			faceIndex++;
   1859 		}
   1860 
   1861 		mesh->numFaces = faceIndex;
   1862 		mesh->numTVFaces = faceIndex;
   1863 
   1864 		aseFace_t *newFaces = ( aseFace_t* )Mem_Alloc( mesh->numFaces * sizeof ( mesh->faces[0] ), TAG_MODEL );
   1865 		memcpy( newFaces, mesh->faces, sizeof( mesh->faces[0] ) * mesh->numFaces );
   1866 		Mem_Free( mesh->faces );
   1867 		mesh->faces = newFaces;
   1868 	}
   1869 
   1870 	return ase;
   1871 }
   1872 
   1873 /*
   1874 =================
   1875 idRenderModelStatic::ConvertMAToModelSurfaces
   1876 =================
   1877 */
   1878 bool idRenderModelStatic::ConvertMAToModelSurfaces (const struct maModel_s *ma ) {
   1879 
   1880 	maObject_t *	object;
   1881 	maMesh_t *		mesh;
   1882 	maMaterial_t *	material;
   1883 	
   1884 	const idMaterial *im1, *im2;
   1885 	srfTriangles_t *tri;
   1886 	int				objectNum;
   1887 	int				i, j, k;
   1888 	int				v, tv;
   1889 	int *			vRemap;
   1890 	int *			tvRemap;
   1891 	matchVert_t *	mvTable;	// all of the match verts
   1892 	matchVert_t **	mvHash;		// points inside mvTable for each xyz index
   1893 	matchVert_t *	lastmv;
   1894 	matchVert_t *	mv;
   1895 	idVec3			normal;
   1896 	float			uOffset, vOffset, textureSin, textureCos;
   1897 	float			uTiling, vTiling;
   1898 	int *			mergeTo;
   1899 	byte *			color;
   1900 	static byte	identityColor[4] = { 255, 255, 255, 255 };
   1901 	modelSurface_t	surf, *modelSurf;
   1902 
   1903 	if ( !ma ) {
   1904 		return false;
   1905 	}
   1906 	if ( ma->objects.Num() < 1 ) {
   1907 		return false;
   1908 	}
   1909 
   1910 	timeStamp = ma->timeStamp;
   1911 
   1912 	// the modeling programs can save out multiple surfaces with a common
   1913 	// material, but we would like to mege them together where possible
   1914 	// meaning that this->NumSurfaces() <= ma->objects.currentElements
   1915 	mergeTo = (int *)_alloca( ma->objects.Num() * sizeof( *mergeTo ) ); 
   1916 
   1917 	surf.geometry = NULL;
   1918 	if ( ma->materials.Num() == 0 ) {
   1919 		// if we don't have any materials, dump everything into a single surface
   1920 		surf.shader = tr.defaultMaterial;
   1921 		surf.id = 0;
   1922 		this->AddSurface( surf );
   1923 		for ( i = 0; i < ma->objects.Num(); i++ ) { 
   1924 			mergeTo[i] = 0;
   1925 		}
   1926 	} else if ( !r_mergeModelSurfaces.GetBool() ) {
   1927 		// don't merge any
   1928 		for ( i = 0; i < ma->objects.Num(); i++ ) { 
   1929 			mergeTo[i] = i;
   1930 			object = ma->objects[i];
   1931 			if(object->materialRef >= 0) {
   1932 				material = ma->materials[object->materialRef];
   1933 				surf.shader = declManager->FindMaterial( material->name );
   1934 			} else {
   1935 				surf.shader = tr.defaultMaterial;
   1936 			}
   1937 			surf.id = this->NumSurfaces();
   1938 			this->AddSurface( surf );
   1939 		}
   1940 	} else {
   1941 		// search for material matches
   1942 		for ( i = 0; i < ma->objects.Num(); i++ ) { 
   1943 			object = ma->objects[i];
   1944 			if(object->materialRef >= 0) {
   1945 				material = ma->materials[object->materialRef];
   1946 				im1 = declManager->FindMaterial( material->name );
   1947 			} else {
   1948 				im1 = tr.defaultMaterial;
   1949 			}
   1950 			if ( im1->IsDiscrete() ) {
   1951 				// flares, autosprites, etc
   1952 				j = this->NumSurfaces();
   1953 			} else {
   1954 				for ( j = 0; j < this->NumSurfaces(); j++ ) {
   1955 					modelSurf = &this->surfaces[j];
   1956 					im2 = modelSurf->shader;
   1957 					if ( im1 == im2 ) {
   1958 						// merge this
   1959 						mergeTo[i] = j;
   1960 						break;
   1961 					}
   1962 				}
   1963 			}
   1964 			if ( j == this->NumSurfaces() ) {
   1965 				// didn't merge
   1966 				mergeTo[i] = j;
   1967 				surf.shader = im1;
   1968 				surf.id = this->NumSurfaces();
   1969 				this->AddSurface( surf );
   1970 			}
   1971 		}
   1972 	}
   1973 
   1974 	idVectorSubset<idVec3, 3> vertexSubset;
   1975 	idVectorSubset<idVec2, 2> texCoordSubset;
   1976 
   1977 	// build the surfaces
   1978 	for ( objectNum = 0; objectNum < ma->objects.Num(); objectNum++ ) {
   1979 		object = ma->objects[objectNum];
   1980 		mesh = &object->mesh;
   1981 		if(object->materialRef >= 0) {
   1982 			material = ma->materials[object->materialRef];
   1983 			im1 = declManager->FindMaterial( material->name );
   1984 		} else {
   1985 			im1 = tr.defaultMaterial;
   1986 		}
   1987 
   1988 		bool normalsParsed = mesh->normalsParsed;
   1989 		
   1990 		// completely ignore any explict normals on surfaces with a renderbump command
   1991 		// which will guarantee the best contours and least vertexes.
   1992 		const char *rb = im1->GetRenderBump();
   1993 		if ( rb != NULL && rb[0] != NULL ) {
   1994 			normalsParsed = false;
   1995 		}
   1996 
   1997 		// It seems like the tools our artists are using often generate
   1998 		// verts and texcoords slightly separated that should be merged
   1999 		// note that we really should combine the surfaces with common materials
   2000 		// before doing this operation, because we can miss a slop combination
   2001 		// if they are in different surfaces
   2002 
   2003 		vRemap = (int *)R_StaticAlloc( mesh->numVertexes * sizeof( vRemap[0] ), TAG_MODEL );
   2004 
   2005 		if ( fastLoad ) {
   2006 			// renderbump doesn't care about vertex count
   2007 			for ( j = 0; j < mesh->numVertexes; j++ ) {
   2008 				vRemap[j] = j;
   2009 			}
   2010 		} else {
   2011 			float vertexEpsilon = r_slopVertex.GetFloat();
   2012 			float expand = 2 * 32 * vertexEpsilon;
   2013 			idVec3 mins, maxs;
   2014 
   2015 			SIMDProcessor->MinMax( mins, maxs, mesh->vertexes, mesh->numVertexes );
   2016 			mins -= idVec3( expand, expand, expand );
   2017 			maxs += idVec3( expand, expand, expand );
   2018 			vertexSubset.Init( mins, maxs, 32, 1024 );
   2019 			for ( j = 0; j < mesh->numVertexes; j++ ) {
   2020 				vRemap[j] = vertexSubset.FindVector( mesh->vertexes, j, vertexEpsilon );
   2021 			}
   2022 		}
   2023 
   2024 		tvRemap = (int *)R_StaticAlloc( mesh->numTVertexes * sizeof( tvRemap[0] ), TAG_MODEL );
   2025 
   2026 		if ( fastLoad ) {
   2027 			// renderbump doesn't care about vertex count
   2028 			for ( j = 0; j < mesh->numTVertexes; j++ ) {
   2029 				tvRemap[j] = j;
   2030 			}
   2031 		} else {
   2032 			float texCoordEpsilon = r_slopTexCoord.GetFloat();
   2033 			float expand = 2 * 32 * texCoordEpsilon;
   2034 			idVec2 mins, maxs;
   2035 
   2036 			SIMDProcessor->MinMax( mins, maxs, mesh->tvertexes, mesh->numTVertexes );
   2037 			mins -= idVec2( expand, expand );
   2038 			maxs += idVec2( expand, expand );
   2039 			texCoordSubset.Init( mins, maxs, 32, 1024 );
   2040 			for ( j = 0; j < mesh->numTVertexes; j++ ) {
   2041 				tvRemap[j] = texCoordSubset.FindVector( mesh->tvertexes, j, texCoordEpsilon );
   2042 			}
   2043 		}
   2044 
   2045 		// we need to find out how many unique vertex / texcoord / color combinations
   2046 		// there are, because MA tracks them separately but we need them unified
   2047 
   2048 		// the maximum possible number of combined vertexes is the number of indexes
   2049 		mvTable = (matchVert_t *)R_ClearedStaticAlloc( mesh->numFaces * 3 * sizeof( mvTable[0] ) );
   2050 
   2051 		// we will have a hash chain based on the xyz values
   2052 		mvHash = (matchVert_t **)R_ClearedStaticAlloc( mesh->numVertexes * sizeof( mvHash[0] ) );
   2053 
   2054 		// allocate triangle surface
   2055 		tri = R_AllocStaticTriSurf();
   2056 		tri->numVerts = 0;
   2057 		tri->numIndexes = 0;
   2058 		R_AllocStaticTriSurfIndexes( tri, mesh->numFaces * 3 );
   2059 		tri->generateNormals = !normalsParsed;
   2060 
   2061 		// init default normal, color and tex coord index
   2062 		normal.Zero();
   2063 		color = identityColor;
   2064 		tv = 0;
   2065 
   2066 		// find all the unique combinations
   2067 		float normalEpsilon = 1.0f - r_slopNormal.GetFloat();
   2068 		for ( j = 0; j < mesh->numFaces; j++ ) {
   2069 			for ( k = 0; k < 3; k++ ) {
   2070 				v = mesh->faces[j].vertexNum[k];
   2071 
   2072 				if ( v < 0 || v >= mesh->numVertexes ) {
   2073 					common->Error( "ConvertMAToModelSurfaces: bad vertex index in MA file %s", name.c_str() );
   2074 				}
   2075 
   2076 				// collapse the position if it was slightly offset 
   2077 				v = vRemap[v];
   2078 
   2079 				// we may or may not have texcoords to compare
   2080 				if ( mesh->numTVertexes != 0 ) {
   2081 					tv = mesh->faces[j].tVertexNum[k];
   2082 					if ( tv < 0 || tv >= mesh->numTVertexes ) {
   2083 						common->Error( "ConvertMAToModelSurfaces: bad tex coord index in MA file %s", name.c_str() );
   2084 					}
   2085 					// collapse the tex coord if it was slightly offset
   2086 					tv = tvRemap[tv];
   2087 				}
   2088 
   2089 				// we may or may not have normals to compare
   2090 				if ( normalsParsed ) {
   2091 					normal = mesh->faces[j].vertexNormals[k];
   2092 				}
   2093 
   2094 				//BSM: Todo: Fix the vertex colors
   2095 				// we may or may not have colors to compare
   2096 				if ( mesh->faces[j].vertexColors[k] != -1 && mesh->faces[j].vertexColors[k] != -999 ) {
   2097 
   2098 					color = &mesh->colors[mesh->faces[j].vertexColors[k]*4];
   2099 				}
   2100 
   2101 				// find a matching vert
   2102 				for ( lastmv = NULL, mv = mvHash[v]; mv != NULL; lastmv = mv, mv = mv->next ) {
   2103 					if ( mv->tv != tv ) {
   2104 						continue;
   2105 					}
   2106 					if ( *(unsigned *)mv->color != *(unsigned *)color ) {
   2107 						continue;
   2108 					}
   2109 					if ( !normalsParsed ) {
   2110 						// if we are going to create the normals, just
   2111 						// matching texcoords is enough
   2112 						break;
   2113 					}
   2114 					if ( mv->normal * normal > normalEpsilon ) {
   2115 						break;		// we already have this one
   2116 					}
   2117 				}
   2118 				if ( !mv ) {
   2119 					// allocate a new match vert and link to hash chain
   2120 					mv = &mvTable[ tri->numVerts ];
   2121 					mv->v = v;
   2122 					mv->tv = tv;
   2123 					mv->normal = normal;
   2124 					*(unsigned *)mv->color = *(unsigned *)color;
   2125 					mv->next = NULL;
   2126 					if ( lastmv ) {
   2127 						lastmv->next = mv;
   2128 					} else {
   2129 						mvHash[v] = mv;
   2130 					}
   2131 					tri->numVerts++;
   2132 				}
   2133 
   2134 				tri->indexes[tri->numIndexes] = mv - mvTable;
   2135 				tri->numIndexes++;
   2136 			}
   2137 		}
   2138 
   2139 		// allocate space for the indexes and copy them
   2140 		if ( tri->numIndexes > mesh->numFaces * 3 ) {
   2141 			common->FatalError( "ConvertMAToModelSurfaces: index miscount in MA file %s", name.c_str() );
   2142 		}
   2143 		if ( tri->numVerts > mesh->numFaces * 3 ) {
   2144 			common->FatalError( "ConvertMAToModelSurfaces: vertex miscount in MA file %s", name.c_str() );
   2145 		}
   2146 
   2147 		// an MA allows the texture coordinates to be scaled, translated, and rotated
   2148 		//BSM: Todo: Does Maya support this and if so how
   2149 		//if ( ase->materials.Num() == 0 ) {
   2150 			uOffset = vOffset = 0.0f;
   2151 			uTiling = vTiling = 1.0f;
   2152 			textureSin = 0.0f;
   2153 			textureCos = 1.0f;
   2154 		//} else {
   2155 		//	material = ase->materials[object->materialRef];
   2156 		//	uOffset = -material->uOffset;
   2157 		//	vOffset = material->vOffset;
   2158 		//	uTiling = material->uTiling;
   2159 		//	vTiling = material->vTiling;
   2160 		//	textureSin = idMath::Sin( material->angle );
   2161 		//	textureCos = idMath::Cos( material->angle );
   2162 		//}
   2163 
   2164 		// now allocate and generate the combined vertexes
   2165 		R_AllocStaticTriSurfVerts( tri, tri->numVerts );
   2166 
   2167 		for ( j = 0; j < tri->numVerts; j++ ) {
   2168 			mv = &mvTable[j];
   2169 			tri->verts[ j ].Clear();
   2170 			tri->verts[ j ].xyz = mesh->vertexes[ mv->v ];
   2171 			tri->verts[ j ].SetNormal( mv->normal );
   2172 			*(unsigned *)tri->verts[j].color = *(unsigned *)mv->color;
   2173 			if ( mesh->numTVertexes != 0 ) {
   2174 				const idVec2 &tv = mesh->tvertexes[ mv->tv ];
   2175 				float u = tv.x * uTiling + uOffset;
   2176 				float v = tv.y * vTiling + vOffset;
   2177 				tri->verts[j].SetTexCoord( u * textureCos + v * textureSin, u * -textureSin + v * textureCos );
   2178 			}
   2179 		}
   2180 
   2181 		R_StaticFree( mvTable );
   2182 		R_StaticFree( mvHash );
   2183 		R_StaticFree( tvRemap );
   2184 		R_StaticFree( vRemap );
   2185 
   2186 		// see if we need to merge with a previous surface of the same material
   2187 		modelSurf = &this->surfaces[mergeTo[ objectNum ]];
   2188 		srfTriangles_t	*mergeTri = modelSurf->geometry;
   2189 		if ( !mergeTri ) {
   2190 			modelSurf->geometry = tri;
   2191 		} else {
   2192 			modelSurf->geometry = R_MergeTriangles( mergeTri, tri );
   2193 			R_FreeStaticTriSurf( tri );
   2194 			R_FreeStaticTriSurf( mergeTri );
   2195 		}
   2196 	}
   2197 
   2198 	return true;
   2199 }
   2200 
   2201 /*
   2202 =================
   2203 idRenderModelStatic::LoadASE
   2204 =================
   2205 */
   2206 bool idRenderModelStatic::LoadASE( const char *fileName ) {
   2207 	aseModel_t *ase;
   2208 
   2209 	ase = ASE_Load( fileName );
   2210 	if ( ase == NULL ) {
   2211 		return false;
   2212 	}
   2213 
   2214 	ConvertASEToModelSurfaces( ase );
   2215 
   2216 	ASE_Free( ase );
   2217 
   2218 	return true;
   2219 }
   2220 
   2221 /*
   2222 =================
   2223 idRenderModelStatic::LoadLWO
   2224 =================
   2225 */
   2226 bool idRenderModelStatic::LoadLWO( const char *fileName ) {
   2227 	unsigned int failID;
   2228 	int failPos;
   2229 	lwObject *lwo;
   2230 
   2231 	lwo = lwGetObject( fileName, &failID, &failPos );
   2232 	if ( lwo == NULL ) {
   2233 		return false;
   2234 	}
   2235 
   2236 	ConvertLWOToModelSurfaces( lwo );
   2237 
   2238 	lwFreeObject( lwo );
   2239 
   2240 	return true;
   2241 }
   2242 
   2243 /*
   2244 =================
   2245 idRenderModelStatic::LoadMA
   2246 =================
   2247 */
   2248 bool idRenderModelStatic::LoadMA( const char *fileName ) {
   2249 	maModel_t *ma;
   2250 
   2251 	ma = MA_Load( fileName );
   2252 	if ( ma == NULL ) {
   2253 		return false;
   2254 	}
   2255 
   2256 	ConvertMAToModelSurfaces( ma );
   2257 
   2258 	MA_Free( ma );
   2259 
   2260 	return true;
   2261 }
   2262 
   2263 
   2264 //=============================================================================
   2265 
   2266 /*
   2267 ================
   2268 idRenderModelStatic::PurgeModel
   2269 ================
   2270 */
   2271 void idRenderModelStatic::PurgeModel() {
   2272 	for ( int i = 0; i < surfaces.Num(); i++ ) {
   2273 		modelSurface_t * surf = &surfaces[i];
   2274 
   2275 		if ( surf->geometry ) {
   2276 			R_FreeStaticTriSurf( surf->geometry );
   2277 		}
   2278 	}
   2279 	surfaces.Clear();
   2280 
   2281 	if ( jointsInverted != NULL ) {
   2282 		Mem_Free( jointsInverted );
   2283 		jointsInverted = NULL;
   2284 	}
   2285 
   2286 	purged = true;
   2287 }
   2288 
   2289 /*
   2290 ==============
   2291 idRenderModelStatic::FreeVertexCache
   2292 
   2293 We are about to restart the vertex cache, so dump everything
   2294 ==============
   2295 */
   2296 void idRenderModelStatic::FreeVertexCache() {
   2297 	for ( int j = 0; j < surfaces.Num(); j++ ) {
   2298 		srfTriangles_t *tri = surfaces[j].geometry;
   2299 		if ( tri == NULL ) {
   2300 			continue;
   2301 		}
   2302 		R_FreeStaticTriSurfVertexCaches( tri );
   2303 	}
   2304 }
   2305 
   2306 /*
   2307 ================
   2308 idRenderModelStatic::ReadFromDemoFile
   2309 ================
   2310 */
   2311 void idRenderModelStatic::ReadFromDemoFile( class idDemoFile *f ) {
   2312 	PurgeModel();
   2313 
   2314 	InitEmpty( f->ReadHashString() );
   2315 
   2316 	int i, j, numSurfaces;
   2317 	f->ReadInt( numSurfaces );
   2318 	
   2319 	for ( i = 0; i < numSurfaces; i++ ) {
   2320 		modelSurface_t	surf;
   2321 		
   2322 		surf.shader = declManager->FindMaterial( f->ReadHashString() );
   2323 		
   2324 		srfTriangles_t	*tri = R_AllocStaticTriSurf();
   2325 		
   2326 		f->ReadInt( tri->numIndexes );
   2327 		R_AllocStaticTriSurfIndexes( tri, tri->numIndexes );
   2328 		for ( j = 0; j < tri->numIndexes; ++j )
   2329 			f->ReadInt( (int&)tri->indexes[j] );
   2330 		
   2331 		f->ReadInt( tri->numVerts );
   2332 		R_AllocStaticTriSurfVerts( tri, tri->numVerts );
   2333 
   2334 		idVec3 tNormal, tTangent, tBiTangent;
   2335 		for ( j = 0; j < tri->numVerts; ++j ) {
   2336 			f->ReadVec3( tri->verts[j].xyz );
   2337 			f->ReadBigArray( tri->verts[j].st, 2 );
   2338 			f->ReadBigArray( tri->verts[j].normal, 4 );
   2339 			f->ReadBigArray( tri->verts[j].tangent, 4 );
   2340 			f->ReadUnsignedChar( tri->verts[j].color[0] );
   2341 			f->ReadUnsignedChar( tri->verts[j].color[1] );
   2342 			f->ReadUnsignedChar( tri->verts[j].color[2] );
   2343 			f->ReadUnsignedChar( tri->verts[j].color[3] );
   2344 		}
   2345 		
   2346 		surf.geometry = tri;
   2347 		
   2348 		this->AddSurface( surf );
   2349 	}
   2350 	this->FinishSurfaces();
   2351 }
   2352 
   2353 /*
   2354 ================
   2355 idRenderModelStatic::WriteToDemoFile
   2356 ================
   2357 */
   2358 void idRenderModelStatic::WriteToDemoFile( class idDemoFile *f ) {
   2359 	int	data[1];
   2360 
   2361 	// note that it has been updated
   2362 	lastArchivedFrame = tr.frameCount;
   2363 
   2364 	data[0] = DC_DEFINE_MODEL;
   2365 	f->WriteInt( data[0] );
   2366 	f->WriteHashString( this->Name() );
   2367 
   2368 	int i, j, iData = surfaces.Num();
   2369 	f->WriteInt( iData );
   2370 
   2371 	for ( i = 0; i < surfaces.Num(); i++ ) {
   2372 		const modelSurface_t	*surf = &surfaces[i];
   2373 		
   2374 		f->WriteHashString( surf->shader->GetName() );
   2375 		
   2376 		srfTriangles_t *tri = surf->geometry;
   2377 		f->WriteInt( tri->numIndexes );
   2378 		for ( j = 0; j < tri->numIndexes; ++j )
   2379 			f->WriteInt( (int&)tri->indexes[j] );
   2380 		f->WriteInt( tri->numVerts );
   2381 		for ( j = 0; j < tri->numVerts; ++j ) {
   2382 			f->WriteVec3( tri->verts[j].xyz );
   2383 			f->WriteBigArray( tri->verts[j].st, 2 );
   2384 			f->WriteBigArray( tri->verts[j].normal, 4 );
   2385 			f->WriteBigArray( tri->verts[j].tangent, 4 );
   2386 			f->WriteUnsignedChar( tri->verts[j].color[0] );
   2387 			f->WriteUnsignedChar( tri->verts[j].color[1] );
   2388 			f->WriteUnsignedChar( tri->verts[j].color[2] );
   2389 			f->WriteUnsignedChar( tri->verts[j].color[3] );
   2390 		}
   2391 	}
   2392 }
   2393 
   2394 /*
   2395 ================
   2396 idRenderModelStatic::IsLoaded
   2397 ================
   2398 */
   2399 bool idRenderModelStatic::IsLoaded() {
   2400 	return !purged;
   2401 }
   2402 
   2403 /*
   2404 ================
   2405 idRenderModelStatic::SetLevelLoadReferenced
   2406 ================
   2407 */
   2408 void idRenderModelStatic::SetLevelLoadReferenced( bool referenced ) {
   2409 	levelLoadReferenced = referenced;
   2410 }
   2411 
   2412 /*
   2413 ================
   2414 idRenderModelStatic::IsLevelLoadReferenced
   2415 ================
   2416 */
   2417 bool idRenderModelStatic::IsLevelLoadReferenced() {
   2418 	return levelLoadReferenced;
   2419 }
   2420 
   2421 /*
   2422 =================
   2423 idRenderModelStatic::TouchData
   2424 =================
   2425 */
   2426 void idRenderModelStatic::TouchData() {
   2427 	for ( int i = 0; i < surfaces.Num(); i++ ) {
   2428 		const modelSurface_t	*surf = &surfaces[i];
   2429 
   2430 		// re-find the material to make sure it gets added to the
   2431 		// level keep list
   2432 		declManager->FindMaterial( surf->shader->GetName() );
   2433 	}
   2434 }
   2435 
   2436 /*
   2437 =================
   2438 idRenderModelStatic::DeleteSurfaceWithId
   2439 =================
   2440 */
   2441 bool idRenderModelStatic::DeleteSurfaceWithId( int id ) {
   2442 	int i;
   2443 
   2444 	for ( i = 0; i < surfaces.Num(); i++ ) {
   2445 		if ( surfaces[i].id == id ) {
   2446 			R_FreeStaticTriSurf( surfaces[i].geometry );
   2447 			surfaces.RemoveIndex( i );
   2448 			return true;
   2449 		}
   2450 	}
   2451 	return false;
   2452 }
   2453 
   2454 /*
   2455 =================
   2456 idRenderModelStatic::DeleteSurfacesWithNegativeId
   2457 =================
   2458 */
   2459 void idRenderModelStatic::DeleteSurfacesWithNegativeId() {
   2460 	for ( int i = 0; i < surfaces.Num(); i++ ) {
   2461 		if ( surfaces[i].id < 0 ) {
   2462 			R_FreeStaticTriSurf( surfaces[i].geometry );
   2463 			surfaces.RemoveIndex( i );
   2464 			i--;
   2465 		}
   2466 	}
   2467 }
   2468 
   2469 /*
   2470 =================
   2471 idRenderModelStatic::FindSurfaceWithId
   2472 =================
   2473 */
   2474 bool idRenderModelStatic::FindSurfaceWithId( int id, int &surfaceNum ) const {
   2475 	for ( int i = 0; i < surfaces.Num(); i++ ) {
   2476 		if ( surfaces[i].id == id ) {
   2477 			surfaceNum = i;
   2478 			return true;
   2479 		}
   2480 	}
   2481 	return false;
   2482 }