DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

CollisionModel_files.cpp (20011B)


      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 /*
     30 ===============================================================================
     31 
     32 	Trace model vs. polygonal model collision detection.
     33 
     34 ===============================================================================
     35 */
     36 
     37 #pragma hdrstop
     38 #include "../idlib/precompiled.h"
     39 
     40 
     41 #include "CollisionModel_local.h"
     42 
     43 #define CM_FILE_EXT			"cm"
     44 #define CM_BINARYFILE_EXT	"bcm"
     45 #define CM_FILEID			"CM"
     46 #define CM_FILEVERSION		"1.00"
     47 
     48 
     49 /*
     50 ===============================================================================
     51 
     52 Writing of collision model file
     53 
     54 ===============================================================================
     55 */
     56 
     57 void CM_GetNodeBounds( idBounds *bounds, cm_node_t *node );
     58 int CM_GetNodeContents( cm_node_t *node );
     59 
     60 
     61 /*
     62 ================
     63 idCollisionModelManagerLocal::WriteNodes
     64 ================
     65 */
     66 void idCollisionModelManagerLocal::WriteNodes( idFile *fp, cm_node_t *node ) {
     67 	fp->WriteFloatString( "\t( %d %f )\n", node->planeType, node->planeDist );
     68 	if ( node->planeType != -1 ) {
     69 		WriteNodes( fp, node->children[0] );
     70 		WriteNodes( fp, node->children[1] );
     71 	}
     72 }
     73 
     74 /*
     75 ================
     76 idCollisionModelManagerLocal::CountPolygonMemory
     77 ================
     78 */
     79 int idCollisionModelManagerLocal::CountPolygonMemory( cm_node_t *node ) const {
     80 	cm_polygonRef_t *pref;
     81 	cm_polygon_t *p;
     82 	int memory;
     83 
     84 	memory = 0;
     85 	for ( pref = node->polygons; pref; pref = pref->next ) {
     86 		p = pref->p;
     87 		if ( p->checkcount == checkCount ) {
     88 			continue;
     89 		}
     90 		p->checkcount = checkCount;
     91 
     92 		memory += sizeof( cm_polygon_t ) + ( p->numEdges - 1 ) * sizeof( p->edges[0] );
     93 	}
     94 	if ( node->planeType != -1 ) {
     95 		memory += CountPolygonMemory( node->children[0] );
     96 		memory += CountPolygonMemory( node->children[1] );
     97 	}
     98 	return memory;
     99 }
    100 
    101 /*
    102 ================
    103 idCollisionModelManagerLocal::WritePolygons
    104 ================
    105 */
    106 void idCollisionModelManagerLocal::WritePolygons( idFile *fp, cm_node_t *node ) {
    107 	cm_polygonRef_t *pref;
    108 	cm_polygon_t *p;
    109 	int i;
    110 
    111 	for ( pref = node->polygons; pref; pref = pref->next ) {
    112 		p = pref->p;
    113 		if ( p->checkcount == checkCount ) {
    114 			continue;
    115 		}
    116 		p->checkcount = checkCount;
    117 		fp->WriteFloatString( "\t%d (", p->numEdges );
    118 		for ( i = 0; i < p->numEdges; i++ ) {
    119 			fp->WriteFloatString( " %d", p->edges[i] );
    120 		}
    121 		fp->WriteFloatString( " ) ( %f %f %f ) %f", p->plane.Normal()[0], p->plane.Normal()[1], p->plane.Normal()[2], p->plane.Dist() );
    122 		fp->WriteFloatString( " ( %f %f %f )", p->bounds[0][0], p->bounds[0][1], p->bounds[0][2] );
    123 		fp->WriteFloatString( " ( %f %f %f )", p->bounds[1][0], p->bounds[1][1], p->bounds[1][2] );
    124 		fp->WriteFloatString( " \"%s\"\n", p->material->GetName() );
    125 	}
    126 	if ( node->planeType != -1 ) {
    127 		WritePolygons( fp, node->children[0] );
    128 		WritePolygons( fp, node->children[1] );
    129 	}
    130 }
    131 
    132 /*
    133 ================
    134 idCollisionModelManagerLocal::CountBrushMemory
    135 ================
    136 */
    137 int idCollisionModelManagerLocal::CountBrushMemory( cm_node_t *node ) const {
    138 	cm_brushRef_t *bref;
    139 	cm_brush_t *b;
    140 	int memory;
    141 
    142 	memory = 0;
    143 	for ( bref = node->brushes; bref; bref = bref->next ) {
    144 		b = bref->b;
    145 		if ( b->checkcount == checkCount ) {
    146 			continue;
    147 		}
    148 		b->checkcount = checkCount;
    149 
    150 		memory += sizeof( cm_brush_t ) + ( b->numPlanes - 1 ) * sizeof( b->planes[0] );
    151 	}
    152 	if ( node->planeType != -1 ) {
    153 		memory += CountBrushMemory( node->children[0] );
    154 		memory += CountBrushMemory( node->children[1] );
    155 	}
    156 	return memory;
    157 }
    158 
    159 /*
    160 ================
    161 idCollisionModelManagerLocal::WriteBrushes
    162 ================
    163 */
    164 void idCollisionModelManagerLocal::WriteBrushes( idFile *fp, cm_node_t *node ) {
    165 	cm_brushRef_t *bref;
    166 	cm_brush_t *b;
    167 	int i;
    168 
    169 	for ( bref = node->brushes; bref; bref = bref->next ) {
    170 		b = bref->b;
    171 		if ( b->checkcount == checkCount ) {
    172 			continue;
    173 		}
    174 		b->checkcount = checkCount;
    175 		fp->WriteFloatString( "\t%d {\n", b->numPlanes );
    176 		for ( i = 0; i < b->numPlanes; i++ ) {
    177 			fp->WriteFloatString( "\t\t( %f %f %f ) %f\n", b->planes[i].Normal()[0], b->planes[i].Normal()[1], b->planes[i].Normal()[2], b->planes[i].Dist() );
    178 		}
    179 		fp->WriteFloatString( "\t} ( %f %f %f )", b->bounds[0][0], b->bounds[0][1], b->bounds[0][2] );
    180 		fp->WriteFloatString( " ( %f %f %f ) \"%s\"\n", b->bounds[1][0], b->bounds[1][1], b->bounds[1][2], StringFromContents( b->contents ) );
    181 	}
    182 	if ( node->planeType != -1 ) {
    183 		WriteBrushes( fp, node->children[0] );
    184 		WriteBrushes( fp, node->children[1] );
    185 	}
    186 }
    187 
    188 /*
    189 ================
    190 idCollisionModelManagerLocal::WriteCollisionModel
    191 ================
    192 */
    193 void idCollisionModelManagerLocal::WriteCollisionModel( idFile *fp, cm_model_t *model ) {
    194 	int i, polygonMemory, brushMemory;
    195 
    196 	fp->WriteFloatString( "collisionModel \"%s\" {\n", model->name.c_str() );
    197 	// vertices
    198 	fp->WriteFloatString( "\tvertices { /* numVertices = */ %d\n", model->numVertices );
    199 	for ( i = 0; i < model->numVertices; i++ ) {
    200 		fp->WriteFloatString( "\t/* %d */ ( %f %f %f )\n", i, model->vertices[i].p[0], model->vertices[i].p[1], model->vertices[i].p[2] );
    201 	}
    202 	fp->WriteFloatString( "\t}\n" );
    203 	// edges
    204 	fp->WriteFloatString( "\tedges { /* numEdges = */ %d\n", model->numEdges );
    205 	for ( i = 0; i < model->numEdges; i++ ) {
    206 		fp->WriteFloatString( "\t/* %d */ ( %d %d ) %d %d\n", i, model->edges[i].vertexNum[0], model->edges[i].vertexNum[1], model->edges[i].internal, model->edges[i].numUsers );
    207 	}
    208 	fp->WriteFloatString( "\t}\n" );
    209 	// nodes
    210 	fp->WriteFloatString( "\tnodes {\n" );
    211 	WriteNodes( fp, model->node );
    212 	fp->WriteFloatString( "\t}\n" );
    213 	// polygons
    214 	checkCount++;
    215 	polygonMemory = CountPolygonMemory( model->node );
    216 	fp->WriteFloatString( "\tpolygons /* polygonMemory = */ %d {\n", polygonMemory );
    217 	checkCount++;
    218 	WritePolygons( fp, model->node );
    219 	fp->WriteFloatString( "\t}\n" );
    220 	// brushes
    221 	checkCount++;
    222 	brushMemory = CountBrushMemory( model->node );
    223 	fp->WriteFloatString( "\tbrushes /* brushMemory = */ %d {\n", brushMemory );
    224 	checkCount++;
    225 	WriteBrushes( fp, model->node );
    226 	fp->WriteFloatString( "\t}\n" );
    227 	// closing brace
    228 	fp->WriteFloatString( "}\n" );
    229 }
    230 
    231 /*
    232 ================
    233 idCollisionModelManagerLocal::WriteCollisionModelsToFile
    234 ================
    235 */
    236 void idCollisionModelManagerLocal::WriteCollisionModelsToFile( const char *filename, int firstModel, int lastModel, unsigned int mapFileCRC ) {
    237 	int i;
    238 	idFile *fp;
    239 	idStr name;
    240 
    241 	name = filename;
    242 	name.SetFileExtension( CM_FILE_EXT );
    243 
    244 	common->Printf( "writing %s\n", name.c_str() );
    245 	fp = fileSystem->OpenFileWrite( name, "fs_basepath" );
    246 	if ( !fp ) {
    247 		common->Warning( "idCollisionModelManagerLocal::WriteCollisionModelsToFile: Error opening file %s\n", name.c_str() );
    248 		return;
    249 	}
    250 
    251 	// write file id and version
    252 	fp->WriteFloatString( "%s \"%s\"\n\n", CM_FILEID, CM_FILEVERSION );
    253 	// write the map file crc
    254 	fp->WriteFloatString( "%u\n\n", mapFileCRC );
    255 
    256 	// write the collision models
    257 	for ( i = firstModel; i < lastModel; i++ ) {
    258 		WriteCollisionModel( fp, models[ i ] );
    259 	}
    260 
    261 	fileSystem->CloseFile( fp );
    262 }
    263 
    264 /*
    265 ================
    266 idCollisionModelManagerLocal::WriteCollisionModelForMapEntity
    267 ================
    268 */
    269 bool idCollisionModelManagerLocal::WriteCollisionModelForMapEntity( const idMapEntity *mapEnt, const char *filename, const bool testTraceModel ) {
    270 	idFile *fp;
    271 	idStr name;
    272 	cm_model_t *model;
    273 
    274 	SetupHash();
    275 	model = CollisionModelForMapEntity( mapEnt );
    276 	model->name = filename;
    277 
    278 	name = filename;
    279 	name.SetFileExtension( CM_FILE_EXT );
    280 
    281 	common->Printf( "writing %s\n", name.c_str() );
    282 	fp = fileSystem->OpenFileWrite( name, "fs_basepath" );
    283 	if ( !fp ) {
    284 		common->Printf( "idCollisionModelManagerLocal::WriteCollisionModelForMapEntity: Error opening file %s\n", name.c_str() );
    285 		FreeModel( model );
    286 		return false;
    287 	}
    288 
    289 	// write file id and version
    290 	fp->WriteFloatString( "%s \"%s\"\n\n", CM_FILEID, CM_FILEVERSION );
    291 	// write the map file crc
    292 	fp->WriteFloatString( "%u\n\n", 0 );
    293 
    294 	// write the collision model
    295 	WriteCollisionModel( fp, model );
    296 
    297 	fileSystem->CloseFile( fp );
    298 
    299 	if ( testTraceModel ) {
    300 		idTraceModel trm;
    301 		TrmFromModel( model, trm );
    302 	}
    303 
    304 	FreeModel( model );
    305 
    306 	return true;
    307 }
    308 
    309 
    310 /*
    311 ===============================================================================
    312 
    313 Loading of collision model file
    314 
    315 ===============================================================================
    316 */
    317 
    318 /*
    319 ================
    320 idCollisionModelManagerLocal::ParseVertices
    321 ================
    322 */
    323 void idCollisionModelManagerLocal::ParseVertices( idLexer *src, cm_model_t *model ) {
    324 	int i;
    325 
    326 	src->ExpectTokenString( "{" );
    327 	model->numVertices = src->ParseInt();
    328 	model->maxVertices = model->numVertices;
    329 	model->vertices = (cm_vertex_t *) Mem_ClearedAlloc( model->maxVertices * sizeof( cm_vertex_t ), TAG_COLLISION );
    330 	for ( i = 0; i < model->numVertices; i++ ) {
    331 		src->Parse1DMatrix( 3, model->vertices[i].p.ToFloatPtr() );
    332 		model->vertices[i].side = 0;
    333 		model->vertices[i].sideSet = 0;
    334 		model->vertices[i].checkcount = 0;
    335 	}
    336 	src->ExpectTokenString( "}" );
    337 }
    338 
    339 /*
    340 ================
    341 idCollisionModelManagerLocal::ParseEdges
    342 ================
    343 */
    344 void idCollisionModelManagerLocal::ParseEdges( idLexer *src, cm_model_t *model ) {
    345 	int i;
    346 
    347 	src->ExpectTokenString( "{" );
    348 	model->numEdges = src->ParseInt();
    349 	model->maxEdges = model->numEdges;
    350 	model->edges = (cm_edge_t *) Mem_ClearedAlloc( model->maxEdges * sizeof( cm_edge_t ), TAG_COLLISION );
    351 	for ( i = 0; i < model->numEdges; i++ ) {
    352 		src->ExpectTokenString( "(" );
    353 		model->edges[i].vertexNum[0] = src->ParseInt();
    354 		model->edges[i].vertexNum[1] = src->ParseInt();
    355 		src->ExpectTokenString( ")" );
    356 		model->edges[i].side = 0;
    357 		model->edges[i].sideSet = 0;
    358 		model->edges[i].internal = src->ParseInt();
    359 		model->edges[i].numUsers = src->ParseInt();
    360 		model->edges[i].normal = vec3_origin;
    361 		model->edges[i].checkcount = 0;
    362 		model->numInternalEdges += model->edges[i].internal;
    363 	}
    364 	src->ExpectTokenString( "}" );
    365 }
    366 
    367 /*
    368 ================
    369 idCollisionModelManagerLocal::ParseNodes
    370 ================
    371 */
    372 cm_node_t *idCollisionModelManagerLocal::ParseNodes( idLexer *src, cm_model_t *model, cm_node_t *parent ) {
    373 	cm_node_t *node;
    374 
    375 	model->numNodes++;
    376 	node = AllocNode( model, model->numNodes < NODE_BLOCK_SIZE_SMALL ? NODE_BLOCK_SIZE_SMALL : NODE_BLOCK_SIZE_LARGE );
    377 	node->brushes = NULL;
    378 	node->polygons = NULL;
    379 	node->parent = parent;
    380 	src->ExpectTokenString( "(" );
    381 	node->planeType = src->ParseInt();
    382 	node->planeDist = src->ParseFloat();
    383 	src->ExpectTokenString( ")" );
    384 	if ( node->planeType != -1 ) {
    385 		node->children[0] = ParseNodes( src, model, node );
    386 		node->children[1] = ParseNodes( src, model, node );
    387 	}
    388 	return node;
    389 }
    390 
    391 /*
    392 ================
    393 idCollisionModelManagerLocal::ParsePolygons
    394 ================
    395 */
    396 void idCollisionModelManagerLocal::ParsePolygons( idLexer *src, cm_model_t *model ) {
    397 	cm_polygon_t *p;
    398 	int i, numEdges;
    399 	idVec3 normal;
    400 	idToken token;
    401 
    402 	if ( src->CheckTokenType( TT_NUMBER, 0, &token ) ) {
    403 		model->polygonBlock = (cm_polygonBlock_t *) Mem_ClearedAlloc( sizeof( cm_polygonBlock_t ) + token.GetIntValue(), TAG_COLLISION );
    404 		model->polygonBlock->bytesRemaining = token.GetIntValue();
    405 		model->polygonBlock->next = ( (byte *) model->polygonBlock ) + sizeof( cm_polygonBlock_t );
    406 	}
    407 
    408 	src->ExpectTokenString( "{" );
    409 	while ( !src->CheckTokenString( "}" ) ) {
    410 		// parse polygon
    411 		numEdges = src->ParseInt();
    412 		p = AllocPolygon( model, numEdges );
    413 		p->numEdges = numEdges;
    414 		src->ExpectTokenString( "(" );
    415 		for ( i = 0; i < p->numEdges; i++ ) {
    416 			p->edges[i] = src->ParseInt();
    417 		}
    418 		src->ExpectTokenString( ")" );
    419 		src->Parse1DMatrix( 3, normal.ToFloatPtr() );
    420 		p->plane.SetNormal( normal );
    421 		p->plane.SetDist( src->ParseFloat() );
    422 		src->Parse1DMatrix( 3, p->bounds[0].ToFloatPtr() );
    423 		src->Parse1DMatrix( 3, p->bounds[1].ToFloatPtr() );
    424 		src->ExpectTokenType( TT_STRING, 0, &token );
    425 		// get material
    426 		p->material = declManager->FindMaterial( token );
    427 		p->contents = p->material->GetContentFlags();
    428 		p->checkcount = 0;
    429 		// filter polygon into tree
    430 		R_FilterPolygonIntoTree( model, model->node, NULL, p );
    431 	}
    432 }
    433 
    434 /*
    435 ================
    436 idCollisionModelManagerLocal::ParseBrushes
    437 ================
    438 */
    439 void idCollisionModelManagerLocal::ParseBrushes( idLexer *src, cm_model_t *model ) {
    440 	cm_brush_t *b;
    441 	int i, numPlanes;
    442 	idVec3 normal;
    443 	idToken token;
    444 
    445 	if ( src->CheckTokenType( TT_NUMBER, 0, &token ) ) {
    446 		model->brushBlock = (cm_brushBlock_t *) Mem_ClearedAlloc( sizeof( cm_brushBlock_t ) + token.GetIntValue(), TAG_COLLISION );
    447 		model->brushBlock->bytesRemaining = token.GetIntValue();
    448 		model->brushBlock->next = ( (byte *) model->brushBlock ) + sizeof( cm_brushBlock_t );
    449 	}
    450 
    451 	src->ExpectTokenString( "{" );
    452 	while ( !src->CheckTokenString( "}" ) ) {
    453 		// parse brush
    454 		numPlanes = src->ParseInt();
    455 		b = AllocBrush( model, numPlanes );
    456 		b->numPlanes = numPlanes;
    457 		src->ExpectTokenString( "{" );
    458 		for ( i = 0; i < b->numPlanes; i++ ) {
    459 			src->Parse1DMatrix( 3, normal.ToFloatPtr() );
    460 			b->planes[i].SetNormal( normal );
    461 			b->planes[i].SetDist( src->ParseFloat() );
    462 		}
    463 		src->ExpectTokenString( "}" );
    464 		src->Parse1DMatrix( 3, b->bounds[0].ToFloatPtr() );
    465 		src->Parse1DMatrix( 3, b->bounds[1].ToFloatPtr() );
    466 		src->ReadToken( &token );
    467 		if ( token.type == TT_NUMBER ) {
    468 			b->contents = token.GetIntValue();		// old .cm files use a single integer
    469 		} else {
    470 			b->contents = ContentsFromString( token );
    471 		}
    472 		b->checkcount = 0;
    473 		b->primitiveNum = 0;
    474 		b->material = NULL;
    475 		// filter brush into tree
    476 		R_FilterBrushIntoTree( model, model->node, NULL, b );
    477 	}
    478 }
    479 
    480 /*
    481 ================
    482 idCollisionModelManagerLocal::ParseCollisionModel
    483 ================
    484 */
    485 cm_model_t * idCollisionModelManagerLocal::ParseCollisionModel( idLexer *src ) {
    486 
    487 	cm_model_t *model;
    488 	idToken token;
    489 
    490 	if ( numModels >= MAX_SUBMODELS ) {
    491 		common->Error( "LoadModel: no free slots" );
    492 		return NULL;
    493 	}
    494 	model = AllocModel();
    495 	models[numModels ] = model;
    496 	numModels++;
    497 	// parse the file
    498 	src->ExpectTokenType( TT_STRING, 0, &token );
    499 	model->name = token;
    500 	src->ExpectTokenString( "{" );
    501 	while ( !src->CheckTokenString( "}" ) ) {
    502 
    503 		src->ReadToken( &token );
    504 
    505 		if ( token == "vertices" ) {
    506 			ParseVertices( src, model );
    507 			continue;
    508 		}
    509 
    510 		if ( token == "edges" ) {
    511 			ParseEdges( src, model );
    512 			continue;
    513 		}
    514 
    515 		if ( token == "nodes" ) {
    516 			src->ExpectTokenString( "{" );
    517 			model->node = ParseNodes( src, model, NULL );
    518 			src->ExpectTokenString( "}" );
    519 			continue;
    520 		}
    521 
    522 		if ( token == "polygons" ) {
    523 			ParsePolygons( src, model );
    524 			continue;
    525 		}
    526 
    527 		if ( token == "brushes" ) {
    528 			ParseBrushes( src, model );
    529 			continue;
    530 		}
    531 
    532 		src->Error( "ParseCollisionModel: bad token \"%s\"", token.c_str() );
    533 	}
    534 	// calculate edge normals
    535 	checkCount++;
    536 	CalculateEdgeNormals( model, model->node );
    537 	// get model bounds from brush and polygon bounds
    538 	CM_GetNodeBounds( &model->bounds, model->node );
    539 	// get model contents
    540 	model->contents = CM_GetNodeContents( model->node );
    541 	// total memory used by this model
    542 	model->usedMemory = model->numVertices * sizeof(cm_vertex_t) +
    543 						model->numEdges * sizeof(cm_edge_t) +
    544 						model->polygonMemory +
    545 						model->brushMemory +
    546 						model->numNodes * sizeof(cm_node_t) +
    547 						model->numPolygonRefs * sizeof(cm_polygonRef_t) +
    548 						model->numBrushRefs * sizeof(cm_brushRef_t);
    549 
    550 	return model;
    551 }
    552 
    553 /*
    554 ================
    555 idCollisionModelManagerLocal::LoadCollisionModelFile
    556 ================
    557 */
    558 bool idCollisionModelManagerLocal::LoadCollisionModelFile( const char *name, unsigned int mapFileCRC ) {
    559 	idToken token;
    560 	idLexer *src;
    561 	unsigned int crc;
    562 
    563 	// load it
    564 	idStrStatic< MAX_OSPATH > fileName = name;
    565 
    566 	// check for generated file
    567 	idStrStatic< MAX_OSPATH > generatedFileName = fileName;
    568 	generatedFileName.Insert( "generated/", 0 );
    569 	generatedFileName.SetFileExtension( CM_BINARYFILE_EXT );
    570 
    571 	// if we are reloading the same map, check the timestamp
    572 	// and try to skip all the work
    573 	ID_TIME_T currentTimeStamp = fileSystem->GetTimestamp( fileName );
    574 
    575 	// see if we have a generated version of this 
    576 	bool loaded = false;
    577 	idFileLocal file( fileSystem->OpenFileReadMemory( generatedFileName ) );
    578 	if ( file != NULL ) {
    579 		int numEntries = 0;
    580 		file->ReadBig( numEntries );
    581 		file->ReadString( mapName );
    582 		file->ReadBig( crc );
    583 		idStrStatic< 32 > fileID;
    584 		idStrStatic< 32 > fileVersion;
    585 		file->ReadString( fileID );
    586 		file->ReadString( fileVersion );
    587 		if ( fileID == CM_FILEID && fileVersion == CM_FILEVERSION && crc == mapFileCRC && numEntries > 0 ) {
    588 			for ( int i = 0; i < numEntries; i++ ) {
    589 				cm_model_t *model = LoadBinaryModelFromFile( file, currentTimeStamp );
    590 				models[ numModels ] = model;
    591 				numModels++;
    592 			}
    593 			loaded = true;
    594 		}
    595 	}
    596 
    597 	if ( !loaded ) {
    598 
    599 		fileName.SetFileExtension( CM_FILE_EXT );
    600 		src = new (TAG_COLLISION) idLexer( fileName );
    601 		src->SetFlags( LEXFL_NOSTRINGCONCAT | LEXFL_NODOLLARPRECOMPILE );
    602 		if ( !src->IsLoaded() ) {
    603 			delete src;
    604 			return false;
    605 		}
    606 
    607 		int numEntries = 0;
    608 		idFileLocal outputFile( fileSystem->OpenFileWrite( generatedFileName, "fs_basepath" ) );
    609 		if ( outputFile != NULL ) {
    610 			outputFile->WriteBig( numEntries );
    611 			outputFile->WriteString( mapName );
    612 			outputFile->WriteBig( mapFileCRC );
    613 			outputFile->WriteString( CM_FILEID );
    614 			outputFile->WriteString( CM_FILEVERSION );
    615 		}
    616 
    617 		if ( !src->ExpectTokenString( CM_FILEID ) ) {
    618 			common->Warning( "%s is not an CM file.", fileName.c_str() );
    619 			delete src;
    620 			return false;
    621 		}
    622 
    623 		if ( !src->ReadToken( &token ) || token != CM_FILEVERSION ) {
    624 			common->Warning( "%s has version %s instead of %s", fileName.c_str(), token.c_str(), CM_FILEVERSION );
    625 			delete src;
    626 			return false;
    627 		}
    628 
    629 		if ( !src->ExpectTokenType( TT_NUMBER, TT_INTEGER, &token ) ) {
    630 			common->Warning( "%s has no map file CRC", fileName.c_str() );
    631 			delete src;
    632 			return false;
    633 		}
    634 
    635 		crc = token.GetUnsignedLongValue();
    636 		if ( mapFileCRC && crc != mapFileCRC ) {
    637 			common->Printf( "%s is out of date\n", fileName.c_str() );
    638 			delete src;
    639 			return false;
    640 		}
    641 
    642 		// parse the file
    643 		while ( 1 ) {
    644 			if ( !src->ReadToken( &token ) ) {
    645 				break;
    646 			}
    647 
    648 			if ( token == "collisionModel" ) {
    649 				cm_model_t *model = ParseCollisionModel( src );
    650 				if ( model == NULL ) {
    651 					delete src;
    652 					return false;
    653 				}
    654 				if ( outputFile != NULL ) {
    655 					WriteBinaryModelToFile( model, outputFile, currentTimeStamp );
    656 					numEntries++;
    657 				}
    658 				continue;
    659 			}
    660 
    661 			src->Error( "idCollisionModelManagerLocal::LoadCollisionModelFile: bad token \"%s\"", token.c_str() );
    662 		}
    663 		delete src;
    664 		if ( outputFile != NULL ) {
    665 			outputFile->Seek( 0, FS_SEEK_SET );
    666 			outputFile->WriteBig( numEntries );
    667 		}
    668 	}
    669 
    670 	return true;
    671 }