]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/tools/compilers/aas/AASBuild.cpp
Various Mac OS X tweaks to get this to build. Probably breaking things.
[icculus/iodoom3.git] / neo / tools / compilers / aas / AASBuild.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 #include "../../../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "AASBuild_local.h"
33
34 #define BFL_PATCH               0x1000
35
36 //===============================================================
37 //
38 //      idAASBuild
39 //
40 //===============================================================
41
42 /*
43 ============
44 idAASBuild::idAASBuild
45 ============
46 */
47 idAASBuild::idAASBuild( void ) {
48         file = NULL;
49         procNodes = NULL;
50         numProcNodes = 0;
51         numGravitationalSubdivisions = 0;
52         numMergedLeafNodes = 0;
53         numLedgeSubdivisions = 0;
54         ledgeMap = NULL;
55 }
56
57 /*
58 ============
59 idAASBuild::~idAASBuild
60 ============
61 */
62 idAASBuild::~idAASBuild( void ) {
63         Shutdown();
64 }
65
66 /*
67 ================
68 idAASBuild::Shutdown
69 ================
70 */
71 void idAASBuild::Shutdown( void ) {
72         aasSettings = NULL;
73         if ( file ) {
74                 delete file;
75                 file = NULL;
76         }
77         DeleteProcBSP();
78         numGravitationalSubdivisions = 0;
79         numMergedLeafNodes = 0;
80         numLedgeSubdivisions = 0;
81         ledgeList.Clear();
82         if ( ledgeMap ) {
83                 delete ledgeMap;
84                 ledgeMap = NULL;
85         }
86 }
87
88 /*
89 ================
90 idAASBuild::ParseProcNodes
91 ================
92 */
93 void idAASBuild::ParseProcNodes( idLexer *src ) {
94         int i;
95
96         src->ExpectTokenString( "{" );
97
98         idAASBuild::numProcNodes = src->ParseInt();
99         if ( idAASBuild::numProcNodes < 0 ) {
100                 src->Error( "idAASBuild::ParseProcNodes: bad numProcNodes" );
101         }
102         idAASBuild::procNodes = (aasProcNode_t *)Mem_ClearedAlloc( idAASBuild::numProcNodes * sizeof( aasProcNode_t ) );
103
104         for ( i = 0; i < idAASBuild::numProcNodes; i++ ) {
105                 aasProcNode_t *node;
106
107                 node = &(idAASBuild::procNodes[i]);
108
109                 src->Parse1DMatrix( 4, node->plane.ToFloatPtr() );
110                 node->children[0] = src->ParseInt();
111                 node->children[1] = src->ParseInt();
112         }
113
114         src->ExpectTokenString( "}" );
115 }
116
117 /*
118 ================
119 idAASBuild::LoadProcBSP
120 ================
121 */
122 bool idAASBuild::LoadProcBSP( const char *name, ID_TIME_T minFileTime ) {
123         idStr fileName;
124         idToken token;
125         idLexer *src;
126
127         // load it
128         fileName = name;
129         fileName.SetFileExtension( PROC_FILE_EXT );
130         src = new idLexer( fileName, LEXFL_NOSTRINGCONCAT | LEXFL_NODOLLARPRECOMPILE );
131         if ( !src->IsLoaded() ) {
132                 common->Warning("idAASBuild::LoadProcBSP: couldn't load %s", fileName.c_str() );
133                 delete src;
134                 return false;
135         }
136
137         // if the file is too old
138         if ( src->GetFileTime() < minFileTime ) {
139                 delete src;
140                 return false;
141         }
142
143         if ( !src->ReadToken( &token ) || token.Icmp( PROC_FILE_ID ) ) {
144                 common->Warning( "idAASBuild::LoadProcBSP: bad id '%s' instead of '%s'", token.c_str(), PROC_FILE_ID );
145                 delete src;
146                 return false;
147         }
148
149         // parse the file
150         while ( 1 ) {
151                 if ( !src->ReadToken( &token ) ) {
152                         break;
153                 }
154
155                 if ( token == "model" ) {
156                         src->SkipBracedSection();
157                         continue;
158                 }
159
160                 if ( token == "shadowModel" ) {
161                         src->SkipBracedSection();
162                         continue;
163                 }
164
165                 if ( token == "interAreaPortals" ) {
166                         src->SkipBracedSection();
167                         continue;
168                 }
169
170                 if ( token == "nodes" ) {
171                         idAASBuild::ParseProcNodes( src );
172                         break;
173                 }
174
175                 src->Error( "idAASBuild::LoadProcBSP: bad token \"%s\"", token.c_str() );
176         }
177
178         delete src;
179
180         return true;
181 }
182
183 /*
184 ============
185 idAASBuild::DeleteProcBSP
186 ============
187 */
188 void idAASBuild::DeleteProcBSP( void ) {
189         if ( procNodes ) {
190                 Mem_Free( procNodes );
191                 procNodes = NULL;
192         }
193         numProcNodes = 0;
194 }
195
196 /*
197 ============
198 idAASBuild::ChoppedAwayByProcBSP
199 ============
200 */
201 bool idAASBuild::ChoppedAwayByProcBSP( int nodeNum, idFixedWinding *w, const idVec3 &normal, const idVec3 &origin, const float radius ) {
202         int res;
203         idFixedWinding back;
204         aasProcNode_t *node;
205         float dist;
206
207         do {
208                 node = idAASBuild::procNodes + nodeNum;
209                 dist = node->plane.Normal() * origin + node->plane[3];
210                 if ( dist > radius ) {
211                         res = SIDE_FRONT;
212                 }
213                 else if ( dist < -radius ) {
214                         res = SIDE_BACK;
215                 }
216                 else {
217                         res = w->Split( &back, node->plane, ON_EPSILON );
218                 }
219                 if ( res == SIDE_FRONT ) {
220                         nodeNum = node->children[0];
221                 }
222                 else if ( res == SIDE_BACK ) {
223                         nodeNum = node->children[1];
224                 }
225                 else if ( res == SIDE_ON ) {
226                         // continue with the side the winding faces
227                         if ( node->plane.Normal() * normal > 0.0f ) {
228                                 nodeNum = node->children[0];
229                         }
230                         else {
231                                 nodeNum = node->children[1];
232                         }
233                 }
234                 else {
235                         // if either node is not solid
236                         if ( node->children[0] < 0 || node->children[1] < 0 ) {
237                                 return false;
238                         }
239                         // only recurse if the node is not solid
240                         if ( node->children[1] > 0 ) {
241                                 if ( !idAASBuild::ChoppedAwayByProcBSP( node->children[1], &back, normal, origin, radius ) ) {
242                                         return false;
243                                 }
244                         }
245                         nodeNum = node->children[0];
246                 }
247         } while ( nodeNum > 0 );
248         if ( nodeNum < 0 ) {
249                 return false;
250         }
251         return true;
252 }
253
254 /*
255 ============
256 idAASBuild::ClipBrushSidesWithProcBSP
257 ============
258 */
259 void idAASBuild::ClipBrushSidesWithProcBSP( idBrushList &brushList ) {
260         int i, clippedSides;
261         idBrush *brush;
262         idFixedWinding neww;
263         idBounds bounds;
264         float radius;
265         idVec3 origin;
266
267         // if the .proc file has no BSP tree
268         if ( idAASBuild::procNodes == NULL ) {
269                 return;
270         }
271
272         clippedSides = 0;
273         for ( brush = brushList.Head(); brush; brush = brush->Next() ) {
274                 for ( i = 0; i < brush->GetNumSides(); i++ ) {
275
276                         if ( !brush->GetSide(i)->GetWinding() ) {
277                                 continue;
278                         }
279
280                         // make a local copy of the winding
281                         neww = *brush->GetSide(i)->GetWinding();
282                         neww.GetBounds( bounds );
283                         origin = (bounds[1] - bounds[0]) * 0.5f;
284                         radius = origin.Length() + ON_EPSILON;
285                         origin = bounds[0] + origin;
286
287                         if ( ChoppedAwayByProcBSP( 0, &neww, brush->GetSide(i)->GetPlane().Normal(), origin, radius ) ) {
288                                 brush->GetSide(i)->SetFlag( SFL_USED_SPLITTER );
289                                 clippedSides++;
290                         }
291                 }
292         }
293
294         common->Printf( "%6d brush sides clipped\n", clippedSides );
295 }
296
297 /*
298 ============
299 idAASBuild::ContentsForAAS
300 ============
301 */
302 int idAASBuild::ContentsForAAS( int contents ) {
303         int c;
304
305         if ( contents & ( CONTENTS_SOLID|CONTENTS_AAS_SOLID|CONTENTS_MONSTERCLIP ) ) {
306                 return AREACONTENTS_SOLID;
307         }
308         c = 0;
309         if ( contents & CONTENTS_WATER ) {
310                 c |= AREACONTENTS_WATER;
311         }
312         if ( contents & CONTENTS_AREAPORTAL ) {
313                 c |= AREACONTENTS_CLUSTERPORTAL;
314         }
315         if ( contents & CONTENTS_AAS_OBSTACLE ) {
316                 c |= AREACONTENTS_OBSTACLE;
317         }
318         return c;
319 }
320
321 /*
322 ============
323 idAASBuild::AddBrushForMapBrush
324 ============
325 */
326 idBrushList idAASBuild::AddBrushesForMapBrush( const idMapBrush *mapBrush, const idVec3 &origin, const idMat3 &axis, int entityNum, int primitiveNum, idBrushList brushList ) {
327         int contents, i;
328         idMapBrushSide *mapSide;
329         const idMaterial *mat;
330         idList<idBrushSide *> sideList;
331         idBrush *brush;
332         idPlane plane;
333
334         contents = 0;
335         for ( i = 0; i < mapBrush->GetNumSides(); i++ ) {
336                 mapSide = mapBrush->GetSide(i);
337                 mat = declManager->FindMaterial( mapSide->GetMaterial() );
338                 contents |= mat->GetContentFlags();
339                 plane = mapSide->GetPlane();
340                 plane.FixDegeneracies( DEGENERATE_DIST_EPSILON );
341                 sideList.Append( new idBrushSide( plane, -1 ) );
342         }
343
344         contents = ContentsForAAS( contents );
345         if ( !contents ) {
346                 for ( i = 0; i < sideList.Num(); i++ ) {
347                         delete sideList[i];
348                 }
349                 return brushList;
350         }
351
352         brush = new idBrush();
353         brush->SetContents( contents );
354
355         if ( !brush->FromSides( sideList ) ) {
356                 common->Warning( "brush primitive %d on entity %d is degenerate", primitiveNum, entityNum );
357                 delete brush;
358                 return brushList;
359         }
360
361         brush->SetEntityNum( entityNum );
362         brush->SetPrimitiveNum( primitiveNum );
363         brush->Transform( origin, axis );
364         brushList.AddToTail( brush );
365
366         return brushList;
367 }
368
369 /*
370 ============
371 idAASBuild::AddBrushesForPatch
372 ============
373 */
374 idBrushList idAASBuild::AddBrushesForMapPatch( const idMapPatch *mapPatch, const idVec3 &origin, const idMat3 &axis, int entityNum, int primitiveNum, idBrushList brushList ) {
375         int i, j, contents, validBrushes;
376         float dot;
377         int v1, v2, v3, v4;
378         idFixedWinding w;
379         idPlane plane;
380         idVec3 d1, d2;
381         idBrush *brush;
382         idSurface_Patch mesh;
383         const idMaterial *mat;
384
385         mat = declManager->FindMaterial( mapPatch->GetMaterial() );
386         contents = ContentsForAAS( mat->GetContentFlags() );
387
388         if ( !contents ) {
389                 return brushList;
390         }
391
392         mesh = idSurface_Patch( *mapPatch );
393
394         // if the patch has an explicit number of subdivisions use it to avoid cracks
395         if ( mapPatch->GetExplicitlySubdivided() ) {
396                 mesh.SubdivideExplicit( mapPatch->GetHorzSubdivisions(), mapPatch->GetVertSubdivisions(), false, true );
397         } else {
398                 mesh.Subdivide( DEFAULT_CURVE_MAX_ERROR_CD, DEFAULT_CURVE_MAX_ERROR_CD, DEFAULT_CURVE_MAX_LENGTH_CD, false );
399         }
400
401         validBrushes = 0;
402
403         for ( i = 0; i < mesh.GetWidth() - 1; i++ ) {
404                 for ( j = 0; j < mesh.GetHeight() - 1; j++ ) {
405
406                         v1 = j * mesh.GetWidth() + i;
407                         v2 = v1 + 1;
408                         v3 = v1 + mesh.GetWidth() + 1;
409                         v4 = v1 + mesh.GetWidth();
410
411                         d1 = mesh[v2].xyz - mesh[v1].xyz;
412                         d2 = mesh[v3].xyz - mesh[v1].xyz;
413                         plane.SetNormal( d1.Cross(d2) );
414                         if ( plane.Normalize() != 0.0f ) {
415                                 plane.FitThroughPoint( mesh[v1].xyz );
416                                 dot = plane.Distance( mesh[v4].xyz );
417                                 // if we can turn it into a quad
418                                 if ( idMath::Fabs(dot) < 0.1f ) {
419                                         w.Clear();
420                                         w += mesh[v1].xyz;
421                                         w += mesh[v2].xyz;
422                                         w += mesh[v3].xyz;
423                                         w += mesh[v4].xyz;
424
425                                         brush = new idBrush();
426                                         brush->SetContents( contents );
427                                         if ( brush->FromWinding( w, plane ) ) {
428                                                 brush->SetEntityNum( entityNum );
429                                                 brush->SetPrimitiveNum( primitiveNum );
430                                                 brush->SetFlag( BFL_PATCH );
431                                                 brush->Transform( origin, axis );
432                                                 brushList.AddToTail( brush );
433                                                 validBrushes++;
434                                         }
435                                         else {
436                                                 delete brush;
437                                         }
438                                         continue;
439                                 }
440                                 else {
441                                         // create one of the triangles
442                                         w.Clear();
443                                         w += mesh[v1].xyz;
444                                         w += mesh[v2].xyz;
445                                         w += mesh[v3].xyz;
446
447                                         brush = new idBrush();
448                                         brush->SetContents( contents );
449                                         if ( brush->FromWinding( w, plane ) ) {
450                                                 brush->SetEntityNum( entityNum );
451                                                 brush->SetPrimitiveNum( primitiveNum );
452                                                 brush->SetFlag( BFL_PATCH );
453                                                 brush->Transform( origin, axis );
454                                                 brushList.AddToTail( brush );
455                                                 validBrushes++;
456                                         }
457                                         else {
458                                                 delete brush;
459                                         }
460                                 }
461                         }
462                         // create the other triangle
463                         d1 = mesh[v3].xyz - mesh[v1].xyz;
464                         d2 = mesh[v4].xyz - mesh[v1].xyz;
465                         plane.SetNormal( d1.Cross(d2) );
466                         if ( plane.Normalize() != 0.0f ) {
467                                 plane.FitThroughPoint( mesh[v1].xyz );
468
469                                 w.Clear();
470                                 w += mesh[v1].xyz;
471                                 w += mesh[v3].xyz;
472                                 w += mesh[v4].xyz;
473
474                                 brush = new idBrush();
475                                 brush->SetContents( contents );
476                                 if ( brush->FromWinding( w, plane ) ) {
477                                         brush->SetEntityNum( entityNum );
478                                         brush->SetPrimitiveNum( primitiveNum );
479                                         brush->SetFlag( BFL_PATCH );
480                                         brush->Transform( origin, axis );
481                                         brushList.AddToTail( brush );
482                                         validBrushes++;
483                                 }
484                                 else {
485                                         delete brush;
486                                 }
487                         }
488                 }
489         }
490
491         if ( !validBrushes ) {
492                 common->Warning( "patch primitive %d on entity %d is completely degenerate", primitiveNum, entityNum );
493         }
494
495         return brushList;
496 }
497
498 /*
499 ============
500 idAASBuild::AddBrushesForMapEntity
501 ============
502 */
503 idBrushList idAASBuild::AddBrushesForMapEntity( const idMapEntity *mapEnt, int entityNum, idBrushList brushList ) {
504         int i;
505         idVec3 origin;
506         idMat3 axis;
507
508         if ( mapEnt->GetNumPrimitives() < 1 ) {
509                 return brushList;
510         }
511
512         mapEnt->epairs.GetVector( "origin", "0 0 0", origin );
513         if ( !mapEnt->epairs.GetMatrix( "rotation", "1 0 0 0 1 0 0 0 1", axis ) ) {
514                 float angle = mapEnt->epairs.GetFloat( "angle" );
515                 if ( angle != 0.0f ) {
516                         axis = idAngles( 0.0f, angle, 0.0f ).ToMat3();
517                 } else {
518                         axis.Identity();
519                 }
520         }
521
522         for ( i = 0; i < mapEnt->GetNumPrimitives(); i++ ) {
523                 idMapPrimitive  *mapPrim;
524
525                 mapPrim = mapEnt->GetPrimitive(i);
526                 if ( mapPrim->GetType() == idMapPrimitive::TYPE_BRUSH ) {
527                         brushList = AddBrushesForMapBrush( static_cast<idMapBrush*>(mapPrim), origin, axis, entityNum, i, brushList );
528                         continue;
529                 }
530                 if ( mapPrim->GetType() == idMapPrimitive::TYPE_PATCH ) {
531                         if ( aasSettings->usePatches ) {
532                                 brushList = AddBrushesForMapPatch( static_cast<idMapPatch*>(mapPrim), origin, axis, entityNum, i, brushList );
533                         }
534                         continue;
535                 }
536         }
537
538         return brushList;
539 }
540
541 /*
542 ============
543 idAASBuild::AddBrushesForMapFile
544 ============
545 */
546 idBrushList idAASBuild::AddBrushesForMapFile( const idMapFile * mapFile, idBrushList brushList ) {
547         int i;
548
549         common->Printf( "[Brush Load]\n" );
550
551         brushList = AddBrushesForMapEntity( mapFile->GetEntity( 0 ), 0, brushList );
552
553         for ( i = 1; i < mapFile->GetNumEntities(); i++ ) {
554                 const char *classname = mapFile->GetEntity( i )->epairs.GetString( "classname" );
555
556                 if ( idStr::Icmp( classname, "func_aas_obstacle" ) == 0 ) {
557                         brushList = AddBrushesForMapEntity( mapFile->GetEntity( i ), i, brushList );
558                 }
559         }
560
561         common->Printf( "%6d brushes\n", brushList.Num() );
562
563         return brushList;
564 }
565
566 /*
567 ============
568 idAASBuild::CheckForEntities
569 ============
570 */
571 bool idAASBuild::CheckForEntities( const idMapFile *mapFile, idStrList &entityClassNames ) const {
572         int             i;
573         idStr   classname;
574
575         com_editors |= EDITOR_AAS;
576
577         for ( i = 0; i < mapFile->GetNumEntities(); i++ ) {
578                 if ( !mapFile->GetEntity(i)->epairs.GetString( "classname", "", classname ) ) {
579                         continue;
580                 }
581
582                 if ( aasSettings->ValidEntity( classname ) ) {
583                         entityClassNames.AddUnique( classname );
584                 }
585         }
586
587         com_editors &= ~EDITOR_AAS;
588
589         return ( entityClassNames.Num() != 0 );
590 }
591
592 /*
593 ============
594 MergeAllowed
595 ============
596 */
597 bool MergeAllowed( idBrush *b1, idBrush *b2 ) {
598         return ( b1->GetContents() == b2->GetContents() && !( ( b1->GetFlags() | b2->GetFlags() ) & BFL_PATCH ) );
599 }
600
601 /*
602 ============
603 ExpandedChopAllowed
604 ============
605 */
606 bool ExpandedChopAllowed( idBrush *b1, idBrush *b2 ) {
607         return ( b1->GetContents() == b2->GetContents() );
608 }
609
610 /*
611 ============
612 ExpandedMergeAllowed
613 ============
614 */
615 bool ExpandedMergeAllowed( idBrush *b1, idBrush *b2 ) {
616         return ( b1->GetContents() == b2->GetContents() );
617 }
618
619 /*
620 ============
621 idAASBuild::ChangeMultipleBoundingBoxContents
622 ============
623 */
624 void idAASBuild::ChangeMultipleBoundingBoxContents_r( idBrushBSPNode *node, int mask ) {
625         while( node ) {
626                 if ( !( node->GetContents() & mask ) ) {
627                         node->SetContents( node->GetContents() & ~AREACONTENTS_SOLID );
628                 }
629                 ChangeMultipleBoundingBoxContents_r( node->GetChild( 0 ), mask );
630                 node = node->GetChild( 1 );
631         }
632 }
633
634 /*
635 ============
636 idAASBuild::Build
637 ============
638 */
639 bool idAASBuild::Build( const idStr &fileName, const idAASSettings *settings ) {
640         int i, bit, mask, startTime;
641         idMapFile * mapFile;
642         idBrushList brushList;
643         idList<idBrushList*> expandedBrushes;
644         idBrush *b;
645         idBrushBSP bsp;
646         idStr name;
647         idAASReach reach;
648         idAASCluster cluster;
649         idStrList entityClassNames;
650
651         startTime = Sys_Milliseconds();
652
653         Shutdown();
654
655         aasSettings = settings;
656
657         name = fileName;
658         name.SetFileExtension( "map" );
659
660         mapFile = new idMapFile;
661         if ( !mapFile->Parse( name ) ) {
662                 delete mapFile;
663                 common->Error( "Couldn't load map file: '%s'", name.c_str() );
664                 return false;
665         }
666
667         // check if this map has any entities that use this AAS file
668         if ( !CheckForEntities( mapFile, entityClassNames ) ) {
669                 delete mapFile;
670                 common->Printf( "no entities in map that use %s\n", settings->fileExtension.c_str() );
671                 return true;
672         }
673
674         // load map file brushes
675         brushList = AddBrushesForMapFile( mapFile, brushList );
676
677         // if empty map
678         if ( brushList.Num() == 0 ) {
679                 delete mapFile;
680                 common->Error( "%s is empty", name.c_str() );
681                 return false;
682         }
683
684         // merge as many brushes as possible before expansion
685         brushList.Merge( MergeAllowed );
686
687         // if there is a .proc file newer than the .map file
688         if ( LoadProcBSP( fileName, mapFile->GetFileTime() ) ) {
689                 ClipBrushSidesWithProcBSP( brushList );
690                 DeleteProcBSP();
691         }
692
693         // make copies of the brush list
694         expandedBrushes.Append( &brushList );
695         for ( i = 1; i < aasSettings->numBoundingBoxes; i++ ) {
696                 expandedBrushes.Append( brushList.Copy() );
697         }
698
699         // expand brushes for the axial bounding boxes
700         mask = AREACONTENTS_SOLID;
701         for ( i = 0; i < expandedBrushes.Num(); i++ ) {
702                 for ( b = expandedBrushes[i]->Head(); b; b = b->Next() ) {
703                         b->ExpandForAxialBox( aasSettings->boundingBoxes[i] );
704                         bit = 1 << ( i + AREACONTENTS_BBOX_BIT );
705                         mask |= bit;
706                         b->SetContents( b->GetContents() | bit );
707                 }
708         }
709
710         // move all brushes back into the original list
711         for ( i = 1; i < aasSettings->numBoundingBoxes; i++ ) {
712                 brushList.AddToTail( *expandedBrushes[i] );
713                 delete expandedBrushes[i];
714         }
715
716         if ( aasSettings->writeBrushMap ) {
717                 bsp.WriteBrushMap( fileName, "_" + aasSettings->fileExtension, AREACONTENTS_SOLID );
718         }
719
720         // build BSP tree from brushes
721         bsp.Build( brushList, AREACONTENTS_SOLID, ExpandedChopAllowed, ExpandedMergeAllowed );
722
723         // only solid nodes with all bits set for all bounding boxes need to stay solid
724         ChangeMultipleBoundingBoxContents_r( bsp.GetRootNode(), mask );
725
726         // portalize the bsp tree
727         bsp.Portalize();
728
729         // remove subspaces not reachable by entities
730         if ( !bsp.RemoveOutside( mapFile, AREACONTENTS_SOLID, entityClassNames ) ) {
731                 bsp.LeakFile( name );
732                 delete mapFile;
733                 common->Printf( "%s has no outside", name.c_str() );
734                 return false;
735         }
736
737         // gravitational subdivision
738         GravitationalSubdivision( bsp );
739
740         // merge portals where possible
741         bsp.MergePortals( AREACONTENTS_SOLID );
742
743         // melt portal windings
744         bsp.MeltPortals( AREACONTENTS_SOLID );
745
746         if ( aasSettings->writeBrushMap ) {
747                 WriteLedgeMap( fileName, "_" + aasSettings->fileExtension + "_ledge" );
748         }
749
750         // ledge subdivisions
751         LedgeSubdivision( bsp );
752
753         // merge leaf nodes
754         MergeLeafNodes( bsp );
755
756         // merge portals where possible
757         bsp.MergePortals( AREACONTENTS_SOLID );
758
759         // melt portal windings
760         bsp.MeltPortals( AREACONTENTS_SOLID );
761
762         // store the file from the bsp tree
763         StoreFile( bsp );
764         file->settings = *aasSettings;
765
766         // calculate reachability
767         reach.Build( mapFile, file );
768
769         // build clusters
770         cluster.Build( file );
771
772         // optimize the file
773         if ( !aasSettings->noOptimize ) {
774                 file->Optimize();
775         }
776
777         // write the file
778         name.SetFileExtension( aasSettings->fileExtension );
779         file->Write( name, mapFile->GetGeometryCRC() );
780
781         // delete the map file
782         delete mapFile;
783
784         common->Printf( "%6d seconds to create AAS\n", (Sys_Milliseconds() - startTime) / 1000 );
785
786         return true;
787 }
788
789 /*
790 ============
791 idAASBuild::BuildReachability
792 ============
793 */
794 bool idAASBuild::BuildReachability( const idStr &fileName, const idAASSettings *settings ) {
795         int startTime;
796         idMapFile * mapFile;
797         idStr name;
798         idAASReach reach;
799         idAASCluster cluster;
800
801         startTime = Sys_Milliseconds();
802
803         aasSettings = settings;
804
805         name = fileName;
806         name.SetFileExtension( "map" );
807
808         mapFile = new idMapFile;
809         if ( !mapFile->Parse( name ) ) {
810                 delete mapFile;
811                 common->Error( "Couldn't load map file: '%s'", name.c_str() );
812                 return false;
813         }
814
815         file = new idAASFileLocal();
816
817         name.SetFileExtension( aasSettings->fileExtension );
818         if ( !file->Load( name, 0 ) ) {
819                 delete mapFile;
820                 common->Error( "Couldn't load AAS file: '%s'", name.c_str() );
821                 return false;
822         }
823
824         file->settings = *aasSettings;
825
826         // calculate reachability
827         reach.Build( mapFile, file );
828
829         // build clusters
830         cluster.Build( file );
831
832         // write the file
833         file->Write( name, mapFile->GetGeometryCRC() );
834
835         // delete the map file
836         delete mapFile;
837
838         common->Printf( "%6d seconds to calculate reachability\n", (Sys_Milliseconds() - startTime) / 1000 );
839
840         return true;
841 }
842
843 /*
844 ============
845 ParseOptions
846 ============
847 */
848 int ParseOptions( const idCmdArgs &args, idAASSettings &settings ) {
849         int i;
850         idStr str;
851
852         for ( i = 1; i < args.Argc(); i++ ) {
853
854                 str = args.Argv( i );
855                 str.StripLeading( '-' );
856
857                 if ( str.Icmp( "usePatches" ) == 0 ) {
858                         settings.usePatches = true;
859                         common->Printf( "usePatches = true\n" );
860                 } else if ( str.Icmp( "writeBrushMap" ) == 0 ) {
861                         settings.writeBrushMap = true;
862                         common->Printf( "writeBrushMap = true\n" );
863                 } else if ( str.Icmp( "playerFlood" ) == 0 ) {
864                         settings.playerFlood = true;
865                         common->Printf( "playerFlood = true\n" );
866                 } else if ( str.Icmp( "noOptimize" ) == 0 ) {
867                         settings.noOptimize = true;
868                         common->Printf( "noOptimize = true\n" );
869                 }
870         }
871         return args.Argc() - 1;
872 }
873
874 /*
875 ============
876 RunAAS_f
877 ============
878 */
879 void RunAAS_f( const idCmdArgs &args ) {
880         int i;
881         idAASBuild aas;
882         idAASSettings settings;
883         idStr mapName;
884
885         if ( args.Argc() <= 1 ) {
886                 common->Printf( "runAAS [options] <mapfile>\n"
887                                         "options:\n"
888                                         "  -usePatches        = use bezier patches for collision detection.\n"
889                                         "  -writeBrushMap     = write a brush map with the AAS geometry.\n"
890                                         "  -playerFlood       = use player spawn points as valid AAS positions.\n" );
891                 return;
892         }
893
894         common->ClearWarnings( "compiling AAS" );
895
896         common->SetRefreshOnPrint( true );
897
898         // get the aas settings definitions
899         const idDict *dict = gameEdit->FindEntityDefDict( "aas_types", false );
900         if ( !dict ) {
901                 common->Error( "Unable to find entityDef for 'aas_types'" );
902         }
903
904         const idKeyValue *kv = dict->MatchPrefix( "type" );
905         while( kv != NULL ) {
906                 const idDict *settingsDict = gameEdit->FindEntityDefDict( kv->GetValue(), false );
907                 if ( !settingsDict ) {
908                         common->Warning( "Unable to find '%s' in def/aas.def", kv->GetValue().c_str() );
909                 } else {
910                         settings.FromDict( kv->GetValue(), settingsDict );
911                         i = ParseOptions( args, settings );
912                         mapName = args.Argv(i);
913                         mapName.BackSlashesToSlashes();
914                         if ( mapName.Icmpn( "maps/", 4 ) != 0 ) {
915                                 mapName = "maps/" + mapName;
916                         }
917                         aas.Build( mapName, &settings );
918                 }
919
920                 kv = dict->MatchPrefix( "type", kv );
921                 if ( kv ) {
922                         common->Printf( "=======================================================\n" );
923                 }
924         }
925         common->SetRefreshOnPrint( false );
926         common->PrintWarnings();
927 }
928
929 /*
930 ============
931 RunAASDir_f
932 ============
933 */
934 void RunAASDir_f( const idCmdArgs &args ) {
935         int i;
936         idAASBuild aas;
937         idAASSettings settings;
938         idFileList *mapFiles;
939
940         if ( args.Argc() <= 1 ) {
941                 common->Printf( "runAASDir <folder>\n" );
942                 return;
943         }
944
945         common->ClearWarnings( "compiling AAS" );
946
947         common->SetRefreshOnPrint( true );
948
949         // get the aas settings definitions
950         const idDict *dict = gameEdit->FindEntityDefDict( "aas_types", false );
951         if ( !dict ) {
952                 common->Error( "Unable to find entityDef for 'aas_types'" );
953         }
954
955         // scan for .map files
956         mapFiles = fileSystem->ListFiles( idStr("maps/") + args.Argv(1), ".map" );
957
958         // create AAS files for all the .map files
959         for ( i = 0; i < mapFiles->GetNumFiles(); i++ ) {
960                 if ( i ) {
961                         common->Printf( "=======================================================\n" );
962                 }
963
964                 const idKeyValue *kv = dict->MatchPrefix( "type" );
965                 while( kv != NULL ) {
966                         const idDict *settingsDict = gameEdit->FindEntityDefDict( kv->GetValue(), false );
967                         if ( !settingsDict ) {
968                                 common->Warning( "Unable to find '%s' in def/aas.def", kv->GetValue().c_str() );
969                         } else {
970                                 settings.FromDict( kv->GetValue(), settingsDict );
971                                 aas.Build( idStr( "maps/" ) + args.Argv( 1 ) + "/" + mapFiles->GetFile( i ), &settings );
972                         }
973
974                         kv = dict->MatchPrefix( "type", kv );
975                         if ( kv ) {
976                                 common->Printf( "=======================================================\n" );
977                         }
978                 }
979         }
980
981         fileSystem->FreeFileList( mapFiles );
982
983         common->SetRefreshOnPrint( false );
984         common->PrintWarnings();
985 }
986
987 /*
988 ============
989 RunReach_f
990 ============
991 */
992 void RunReach_f( const idCmdArgs &args ) {
993         int i;
994         idAASBuild aas;
995         idAASSettings settings;
996
997         if ( args.Argc() <= 1 ) {
998                 common->Printf( "runReach [options] <mapfile>\n" );
999                 return;
1000         }
1001
1002         common->ClearWarnings( "calculating AAS reachability" );
1003
1004         common->SetRefreshOnPrint( true );
1005
1006         // get the aas settings definitions
1007         const idDict *dict = gameEdit->FindEntityDefDict( "aas_types", false );
1008         if ( !dict ) {
1009                 common->Error( "Unable to find entityDef for 'aas_types'" );
1010         }
1011
1012         const idKeyValue *kv = dict->MatchPrefix( "type" );
1013         while( kv != NULL ) {
1014                 const idDict *settingsDict = gameEdit->FindEntityDefDict( kv->GetValue(), false );
1015                 if ( !settingsDict ) {
1016                         common->Warning( "Unable to find '%s' in def/aas.def", kv->GetValue().c_str() );
1017                 } else {
1018                         settings.FromDict( kv->GetValue(), settingsDict );
1019                         i = ParseOptions( args, settings );
1020                         aas.BuildReachability( idStr("maps/") + args.Argv(i), &settings );
1021                 }
1022
1023                 kv = dict->MatchPrefix( "type", kv );
1024                 if ( kv ) {
1025                         common->Printf( "=======================================================\n" );
1026                 }
1027         }
1028
1029         common->SetRefreshOnPrint( false );
1030         common->PrintWarnings();
1031 }