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