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