CollisionModel_files.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 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 Source Code. If not, please request a copy in writing from id Software at the address below.
  17. 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.
  18. ===========================================================================
  19. */
  20. /*
  21. ===============================================================================
  22. Trace model vs. polygonal model collision detection.
  23. ===============================================================================
  24. */
  25. #include "../idlib/precompiled.h"
  26. #pragma hdrstop
  27. #include "CollisionModel_local.h"
  28. #define CM_FILE_EXT "cm"
  29. #define CM_FILEID "CM"
  30. #define CM_FILEVERSION "1.00"
  31. /*
  32. ===============================================================================
  33. Writing of collision model file
  34. ===============================================================================
  35. */
  36. void CM_GetNodeBounds( idBounds *bounds, cm_node_t *node );
  37. int CM_GetNodeContents( cm_node_t *node );
  38. /*
  39. ================
  40. idCollisionModelManagerLocal::WriteNodes
  41. ================
  42. */
  43. void idCollisionModelManagerLocal::WriteNodes( idFile *fp, cm_node_t *node ) {
  44. fp->WriteFloatString( "\t( %d %f )\n", node->planeType, node->planeDist );
  45. if ( node->planeType != -1 ) {
  46. WriteNodes( fp, node->children[0] );
  47. WriteNodes( fp, node->children[1] );
  48. }
  49. }
  50. /*
  51. ================
  52. idCollisionModelManagerLocal::CountPolygonMemory
  53. ================
  54. */
  55. int idCollisionModelManagerLocal::CountPolygonMemory( cm_node_t *node ) const {
  56. cm_polygonRef_t *pref;
  57. cm_polygon_t *p;
  58. int memory;
  59. memory = 0;
  60. for ( pref = node->polygons; pref; pref = pref->next ) {
  61. p = pref->p;
  62. if ( p->checkcount == checkCount ) {
  63. continue;
  64. }
  65. p->checkcount = checkCount;
  66. memory += sizeof( cm_polygon_t ) + ( p->numEdges - 1 ) * sizeof( p->edges[0] );
  67. }
  68. if ( node->planeType != -1 ) {
  69. memory += CountPolygonMemory( node->children[0] );
  70. memory += CountPolygonMemory( node->children[1] );
  71. }
  72. return memory;
  73. }
  74. /*
  75. ================
  76. idCollisionModelManagerLocal::WritePolygons
  77. ================
  78. */
  79. void idCollisionModelManagerLocal::WritePolygons( idFile *fp, cm_node_t *node ) {
  80. cm_polygonRef_t *pref;
  81. cm_polygon_t *p;
  82. int i;
  83. for ( pref = node->polygons; pref; pref = pref->next ) {
  84. p = pref->p;
  85. if ( p->checkcount == checkCount ) {
  86. continue;
  87. }
  88. p->checkcount = checkCount;
  89. fp->WriteFloatString( "\t%d (", p->numEdges );
  90. for ( i = 0; i < p->numEdges; i++ ) {
  91. fp->WriteFloatString( " %d", p->edges[i] );
  92. }
  93. fp->WriteFloatString( " ) ( %f %f %f ) %f", p->plane.Normal()[0], p->plane.Normal()[1], p->plane.Normal()[2], p->plane.Dist() );
  94. fp->WriteFloatString( " ( %f %f %f )", p->bounds[0][0], p->bounds[0][1], p->bounds[0][2] );
  95. fp->WriteFloatString( " ( %f %f %f )", p->bounds[1][0], p->bounds[1][1], p->bounds[1][2] );
  96. fp->WriteFloatString( " \"%s\"\n", p->material->GetName() );
  97. }
  98. if ( node->planeType != -1 ) {
  99. WritePolygons( fp, node->children[0] );
  100. WritePolygons( fp, node->children[1] );
  101. }
  102. }
  103. /*
  104. ================
  105. idCollisionModelManagerLocal::CountBrushMemory
  106. ================
  107. */
  108. int idCollisionModelManagerLocal::CountBrushMemory( cm_node_t *node ) const {
  109. cm_brushRef_t *bref;
  110. cm_brush_t *b;
  111. int memory;
  112. memory = 0;
  113. for ( bref = node->brushes; bref; bref = bref->next ) {
  114. b = bref->b;
  115. if ( b->checkcount == checkCount ) {
  116. continue;
  117. }
  118. b->checkcount = checkCount;
  119. memory += sizeof( cm_brush_t ) + ( b->numPlanes - 1 ) * sizeof( b->planes[0] );
  120. }
  121. if ( node->planeType != -1 ) {
  122. memory += CountBrushMemory( node->children[0] );
  123. memory += CountBrushMemory( node->children[1] );
  124. }
  125. return memory;
  126. }
  127. /*
  128. ================
  129. idCollisionModelManagerLocal::WriteBrushes
  130. ================
  131. */
  132. void idCollisionModelManagerLocal::WriteBrushes( idFile *fp, cm_node_t *node ) {
  133. cm_brushRef_t *bref;
  134. cm_brush_t *b;
  135. int i;
  136. for ( bref = node->brushes; bref; bref = bref->next ) {
  137. b = bref->b;
  138. if ( b->checkcount == checkCount ) {
  139. continue;
  140. }
  141. b->checkcount = checkCount;
  142. fp->WriteFloatString( "\t%d {\n", b->numPlanes );
  143. for ( i = 0; i < b->numPlanes; i++ ) {
  144. 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() );
  145. }
  146. fp->WriteFloatString( "\t} ( %f %f %f )", b->bounds[0][0], b->bounds[0][1], b->bounds[0][2] );
  147. fp->WriteFloatString( " ( %f %f %f ) \"%s\"\n", b->bounds[1][0], b->bounds[1][1], b->bounds[1][2], StringFromContents( b->contents ) );
  148. }
  149. if ( node->planeType != -1 ) {
  150. WriteBrushes( fp, node->children[0] );
  151. WriteBrushes( fp, node->children[1] );
  152. }
  153. }
  154. /*
  155. ================
  156. idCollisionModelManagerLocal::WriteCollisionModel
  157. ================
  158. */
  159. void idCollisionModelManagerLocal::WriteCollisionModel( idFile *fp, cm_model_t *model ) {
  160. int i, polygonMemory, brushMemory;
  161. fp->WriteFloatString( "collisionModel \"%s\" {\n", model->name.c_str() );
  162. // vertices
  163. fp->WriteFloatString( "\tvertices { /* numVertices = */ %d\n", model->numVertices );
  164. for ( i = 0; i < model->numVertices; i++ ) {
  165. fp->WriteFloatString( "\t/* %d */ ( %f %f %f )\n", i, model->vertices[i].p[0], model->vertices[i].p[1], model->vertices[i].p[2] );
  166. }
  167. fp->WriteFloatString( "\t}\n" );
  168. // edges
  169. fp->WriteFloatString( "\tedges { /* numEdges = */ %d\n", model->numEdges );
  170. for ( i = 0; i < model->numEdges; i++ ) {
  171. 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 );
  172. }
  173. fp->WriteFloatString( "\t}\n" );
  174. // nodes
  175. fp->WriteFloatString( "\tnodes {\n" );
  176. WriteNodes( fp, model->node );
  177. fp->WriteFloatString( "\t}\n" );
  178. // polygons
  179. checkCount++;
  180. polygonMemory = CountPolygonMemory( model->node );
  181. fp->WriteFloatString( "\tpolygons /* polygonMemory = */ %d {\n", polygonMemory );
  182. checkCount++;
  183. WritePolygons( fp, model->node );
  184. fp->WriteFloatString( "\t}\n" );
  185. // brushes
  186. checkCount++;
  187. brushMemory = CountBrushMemory( model->node );
  188. fp->WriteFloatString( "\tbrushes /* brushMemory = */ %d {\n", brushMemory );
  189. checkCount++;
  190. WriteBrushes( fp, model->node );
  191. fp->WriteFloatString( "\t}\n" );
  192. // closing brace
  193. fp->WriteFloatString( "}\n" );
  194. }
  195. /*
  196. ================
  197. idCollisionModelManagerLocal::WriteCollisionModelsToFile
  198. ================
  199. */
  200. void idCollisionModelManagerLocal::WriteCollisionModelsToFile( const char *filename, int firstModel, int lastModel, unsigned int mapFileCRC ) {
  201. int i;
  202. idFile *fp;
  203. idStr name;
  204. name = filename;
  205. name.SetFileExtension( CM_FILE_EXT );
  206. common->Printf( "writing %s\n", name.c_str() );
  207. // _D3XP was saving to fs_cdpath
  208. fp = fileSystem->OpenFileWrite( name, "fs_devpath" );
  209. if ( !fp ) {
  210. common->Warning( "idCollisionModelManagerLocal::WriteCollisionModelsToFile: Error opening file %s\n", name.c_str() );
  211. return;
  212. }
  213. // write file id and version
  214. fp->WriteFloatString( "%s \"%s\"\n\n", CM_FILEID, CM_FILEVERSION );
  215. // write the map file crc
  216. fp->WriteFloatString( "%u\n\n", mapFileCRC );
  217. // write the collision models
  218. for ( i = firstModel; i < lastModel; i++ ) {
  219. WriteCollisionModel( fp, models[ i ] );
  220. }
  221. fileSystem->CloseFile( fp );
  222. }
  223. /*
  224. ================
  225. idCollisionModelManagerLocal::WriteCollisionModelForMapEntity
  226. ================
  227. */
  228. bool idCollisionModelManagerLocal::WriteCollisionModelForMapEntity( const idMapEntity *mapEnt, const char *filename, const bool testTraceModel ) {
  229. idFile *fp;
  230. idStr name;
  231. cm_model_t *model;
  232. SetupHash();
  233. model = CollisionModelForMapEntity( mapEnt );
  234. model->name = filename;
  235. name = filename;
  236. name.SetFileExtension( CM_FILE_EXT );
  237. common->Printf( "writing %s\n", name.c_str() );
  238. fp = fileSystem->OpenFileWrite( name, "fs_devpath" );
  239. if ( !fp ) {
  240. common->Printf( "idCollisionModelManagerLocal::WriteCollisionModelForMapEntity: Error opening file %s\n", name.c_str() );
  241. FreeModel( model );
  242. return false;
  243. }
  244. // write file id and version
  245. fp->WriteFloatString( "%s \"%s\"\n\n", CM_FILEID, CM_FILEVERSION );
  246. // write the map file crc
  247. fp->WriteFloatString( "%u\n\n", 0 );
  248. // write the collision model
  249. WriteCollisionModel( fp, model );
  250. fileSystem->CloseFile( fp );
  251. if ( testTraceModel ) {
  252. idTraceModel trm;
  253. TrmFromModel( model, trm );
  254. }
  255. FreeModel( model );
  256. return true;
  257. }
  258. /*
  259. ===============================================================================
  260. Loading of collision model file
  261. ===============================================================================
  262. */
  263. /*
  264. ================
  265. idCollisionModelManagerLocal::ParseVertices
  266. ================
  267. */
  268. void idCollisionModelManagerLocal::ParseVertices( idLexer *src, cm_model_t *model ) {
  269. int i;
  270. src->ExpectTokenString( "{" );
  271. model->numVertices = src->ParseInt();
  272. model->maxVertices = model->numVertices;
  273. model->vertices = (cm_vertex_t *) Mem_Alloc( model->maxVertices * sizeof( cm_vertex_t ) );
  274. for ( i = 0; i < model->numVertices; i++ ) {
  275. src->Parse1DMatrix( 3, model->vertices[i].p.ToFloatPtr() );
  276. model->vertices[i].side = 0;
  277. model->vertices[i].sideSet = 0;
  278. model->vertices[i].checkcount = 0;
  279. }
  280. src->ExpectTokenString( "}" );
  281. }
  282. /*
  283. ================
  284. idCollisionModelManagerLocal::ParseEdges
  285. ================
  286. */
  287. void idCollisionModelManagerLocal::ParseEdges( idLexer *src, cm_model_t *model ) {
  288. int i;
  289. src->ExpectTokenString( "{" );
  290. model->numEdges = src->ParseInt();
  291. model->maxEdges = model->numEdges;
  292. model->edges = (cm_edge_t *) Mem_Alloc( model->maxEdges * sizeof( cm_edge_t ) );
  293. for ( i = 0; i < model->numEdges; i++ ) {
  294. src->ExpectTokenString( "(" );
  295. model->edges[i].vertexNum[0] = src->ParseInt();
  296. model->edges[i].vertexNum[1] = src->ParseInt();
  297. src->ExpectTokenString( ")" );
  298. model->edges[i].side = 0;
  299. model->edges[i].sideSet = 0;
  300. model->edges[i].internal = src->ParseInt();
  301. model->edges[i].numUsers = src->ParseInt();
  302. model->edges[i].normal = vec3_origin;
  303. model->edges[i].checkcount = 0;
  304. model->numInternalEdges += model->edges[i].internal;
  305. }
  306. src->ExpectTokenString( "}" );
  307. }
  308. /*
  309. ================
  310. idCollisionModelManagerLocal::ParseNodes
  311. ================
  312. */
  313. cm_node_t *idCollisionModelManagerLocal::ParseNodes( idLexer *src, cm_model_t *model, cm_node_t *parent ) {
  314. cm_node_t *node;
  315. model->numNodes++;
  316. node = AllocNode( model, model->numNodes < NODE_BLOCK_SIZE_SMALL ? NODE_BLOCK_SIZE_SMALL : NODE_BLOCK_SIZE_LARGE );
  317. node->brushes = NULL;
  318. node->polygons = NULL;
  319. node->parent = parent;
  320. src->ExpectTokenString( "(" );
  321. node->planeType = src->ParseInt();
  322. node->planeDist = src->ParseFloat();
  323. src->ExpectTokenString( ")" );
  324. if ( node->planeType != -1 ) {
  325. node->children[0] = ParseNodes( src, model, node );
  326. node->children[1] = ParseNodes( src, model, node );
  327. }
  328. return node;
  329. }
  330. /*
  331. ================
  332. idCollisionModelManagerLocal::ParsePolygons
  333. ================
  334. */
  335. void idCollisionModelManagerLocal::ParsePolygons( idLexer *src, cm_model_t *model ) {
  336. cm_polygon_t *p;
  337. int i, numEdges;
  338. idVec3 normal;
  339. idToken token;
  340. if ( src->CheckTokenType( TT_NUMBER, 0, &token ) ) {
  341. model->polygonBlock = (cm_polygonBlock_t *) Mem_Alloc( sizeof( cm_polygonBlock_t ) + token.GetIntValue() );
  342. model->polygonBlock->bytesRemaining = token.GetIntValue();
  343. model->polygonBlock->next = ( (byte *) model->polygonBlock ) + sizeof( cm_polygonBlock_t );
  344. }
  345. src->ExpectTokenString( "{" );
  346. while ( !src->CheckTokenString( "}" ) ) {
  347. // parse polygon
  348. numEdges = src->ParseInt();
  349. p = AllocPolygon( model, numEdges );
  350. p->numEdges = numEdges;
  351. src->ExpectTokenString( "(" );
  352. for ( i = 0; i < p->numEdges; i++ ) {
  353. p->edges[i] = src->ParseInt();
  354. }
  355. src->ExpectTokenString( ")" );
  356. src->Parse1DMatrix( 3, normal.ToFloatPtr() );
  357. p->plane.SetNormal( normal );
  358. p->plane.SetDist( src->ParseFloat() );
  359. src->Parse1DMatrix( 3, p->bounds[0].ToFloatPtr() );
  360. src->Parse1DMatrix( 3, p->bounds[1].ToFloatPtr() );
  361. src->ExpectTokenType( TT_STRING, 0, &token );
  362. // get material
  363. p->material = declManager->FindMaterial( token );
  364. p->contents = p->material->GetContentFlags();
  365. p->checkcount = 0;
  366. // filter polygon into tree
  367. R_FilterPolygonIntoTree( model, model->node, NULL, p );
  368. }
  369. }
  370. /*
  371. ================
  372. idCollisionModelManagerLocal::ParseBrushes
  373. ================
  374. */
  375. void idCollisionModelManagerLocal::ParseBrushes( idLexer *src, cm_model_t *model ) {
  376. cm_brush_t *b;
  377. int i, numPlanes;
  378. idVec3 normal;
  379. idToken token;
  380. if ( src->CheckTokenType( TT_NUMBER, 0, &token ) ) {
  381. model->brushBlock = (cm_brushBlock_t *) Mem_Alloc( sizeof( cm_brushBlock_t ) + token.GetIntValue() );
  382. model->brushBlock->bytesRemaining = token.GetIntValue();
  383. model->brushBlock->next = ( (byte *) model->brushBlock ) + sizeof( cm_brushBlock_t );
  384. }
  385. src->ExpectTokenString( "{" );
  386. while ( !src->CheckTokenString( "}" ) ) {
  387. // parse brush
  388. numPlanes = src->ParseInt();
  389. b = AllocBrush( model, numPlanes );
  390. b->numPlanes = numPlanes;
  391. src->ExpectTokenString( "{" );
  392. for ( i = 0; i < b->numPlanes; i++ ) {
  393. src->Parse1DMatrix( 3, normal.ToFloatPtr() );
  394. b->planes[i].SetNormal( normal );
  395. b->planes[i].SetDist( src->ParseFloat() );
  396. }
  397. src->ExpectTokenString( "}" );
  398. src->Parse1DMatrix( 3, b->bounds[0].ToFloatPtr() );
  399. src->Parse1DMatrix( 3, b->bounds[1].ToFloatPtr() );
  400. src->ReadToken( &token );
  401. if ( token.type == TT_NUMBER ) {
  402. b->contents = token.GetIntValue(); // old .cm files use a single integer
  403. } else {
  404. b->contents = ContentsFromString( token );
  405. }
  406. b->checkcount = 0;
  407. b->primitiveNum = 0;
  408. // filter brush into tree
  409. R_FilterBrushIntoTree( model, model->node, NULL, b );
  410. }
  411. }
  412. /*
  413. ================
  414. idCollisionModelManagerLocal::ParseCollisionModel
  415. ================
  416. */
  417. bool idCollisionModelManagerLocal::ParseCollisionModel( idLexer *src ) {
  418. cm_model_t *model;
  419. idToken token;
  420. if ( numModels >= MAX_SUBMODELS ) {
  421. common->Error( "LoadModel: no free slots" );
  422. return false;
  423. }
  424. model = AllocModel();
  425. models[numModels ] = model;
  426. numModels++;
  427. // parse the file
  428. src->ExpectTokenType( TT_STRING, 0, &token );
  429. model->name = token;
  430. src->ExpectTokenString( "{" );
  431. while ( !src->CheckTokenString( "}" ) ) {
  432. src->ReadToken( &token );
  433. if ( token == "vertices" ) {
  434. ParseVertices( src, model );
  435. continue;
  436. }
  437. if ( token == "edges" ) {
  438. ParseEdges( src, model );
  439. continue;
  440. }
  441. if ( token == "nodes" ) {
  442. src->ExpectTokenString( "{" );
  443. model->node = ParseNodes( src, model, NULL );
  444. src->ExpectTokenString( "}" );
  445. continue;
  446. }
  447. if ( token == "polygons" ) {
  448. ParsePolygons( src, model );
  449. continue;
  450. }
  451. if ( token == "brushes" ) {
  452. ParseBrushes( src, model );
  453. continue;
  454. }
  455. src->Error( "ParseCollisionModel: bad token \"%s\"", token.c_str() );
  456. }
  457. // calculate edge normals
  458. checkCount++;
  459. CalculateEdgeNormals( model, model->node );
  460. // get model bounds from brush and polygon bounds
  461. CM_GetNodeBounds( &model->bounds, model->node );
  462. // get model contents
  463. model->contents = CM_GetNodeContents( model->node );
  464. // total memory used by this model
  465. model->usedMemory = model->numVertices * sizeof(cm_vertex_t) +
  466. model->numEdges * sizeof(cm_edge_t) +
  467. model->polygonMemory +
  468. model->brushMemory +
  469. model->numNodes * sizeof(cm_node_t) +
  470. model->numPolygonRefs * sizeof(cm_polygonRef_t) +
  471. model->numBrushRefs * sizeof(cm_brushRef_t);
  472. return true;
  473. }
  474. /*
  475. ================
  476. idCollisionModelManagerLocal::LoadCollisionModelFile
  477. ================
  478. */
  479. bool idCollisionModelManagerLocal::LoadCollisionModelFile( const char *name, unsigned int mapFileCRC ) {
  480. idStr fileName;
  481. idToken token;
  482. idLexer *src;
  483. unsigned int crc;
  484. // load it
  485. fileName = name;
  486. fileName.SetFileExtension( CM_FILE_EXT );
  487. src = new idLexer( fileName );
  488. src->SetFlags( LEXFL_NOSTRINGCONCAT | LEXFL_NODOLLARPRECOMPILE );
  489. if ( !src->IsLoaded() ) {
  490. delete src;
  491. return false;
  492. }
  493. if ( !src->ExpectTokenString( CM_FILEID ) ) {
  494. common->Warning( "%s is not an CM file.", fileName.c_str() );
  495. delete src;
  496. return false;
  497. }
  498. if ( !src->ReadToken( &token ) || token != CM_FILEVERSION ) {
  499. common->Warning( "%s has version %s instead of %s", fileName.c_str(), token.c_str(), CM_FILEVERSION );
  500. delete src;
  501. return false;
  502. }
  503. if ( !src->ExpectTokenType( TT_NUMBER, TT_INTEGER, &token ) ) {
  504. common->Warning( "%s has no map file CRC", fileName.c_str() );
  505. delete src;
  506. return false;
  507. }
  508. crc = token.GetUnsignedLongValue();
  509. if ( mapFileCRC && crc != mapFileCRC ) {
  510. common->Printf( "%s is out of date\n", fileName.c_str() );
  511. delete src;
  512. return false;
  513. }
  514. // parse the file
  515. while ( 1 ) {
  516. if ( !src->ReadToken( &token ) ) {
  517. break;
  518. }
  519. if ( token == "collisionModel" ) {
  520. if ( !ParseCollisionModel( src ) ) {
  521. delete src;
  522. return false;
  523. }
  524. continue;
  525. }
  526. src->Error( "idCollisionModelManagerLocal::LoadCollisionModelFile: bad token \"%s\"", token.c_str() );
  527. }
  528. delete src;
  529. return true;
  530. }