/* =========================================================================== Return to Castle Wolfenstein single player GPL Source Code Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”). RTCW SP Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. RTCW SP Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RTCW SP Source Code. If not, see . In addition, the RTCW SP 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 RTCW SP Source Code. If not, please request a copy in writing from id Software at the address below. 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. =========================================================================== */ //=========================================================================== // // Name: BSP tool // Function: // Programmer: id Software & Mr Elusive (MrElusive@demigod.demon.nl) // Last update: 1997-12-04 // Tab Size: 3 // Notes: Microsoft Visual C++ optimizations: // "global optimization" or "full optimization" results // in micro brushes?? //=========================================================================== #if defined( WIN32 ) || defined( _WIN32 ) #include #include #include #include #else #include #include #include #include #endif #include "qbsp.h" #include "l_mem.h" //#include "l_qfiles.h" #include "../botlib/aasfile.h" #include "../botlib/be_aas_cluster.h" #include "../botlib/be_aas_optimize.h" #include "aas_create.h" #include "aas_store.h" #include "aas_file.h" #include "aas_cfg.h" #include "be_aas_bspc.h" #define BSPC_VERSION "2.1c-wolf" // TTimo: some messy config things #ifndef BSPC #define BSPC // RF #endif #define stricmp strcasecmp void AAS_InitBotImport(); void AAS_InitClustering(); void AAS_ShowTotals(); extern int use_nodequeue; //brushbsp.c extern int calcgrapplereach; //be_aas_reach.c float subdivide_size = 240; char source[1024]; char name[1024]; vec_t microvolume = 1.0; char outbase[32]; int entity_num; aas_settings_t aassettings; qboolean noprune; //don't prune nodes (bspc.c) qboolean glview; //create a gl view qboolean nodetail; //don't use detail brushes (map.c) qboolean fulldetail; //use but don't mark detail brushes (map.c) qboolean onlyents; //only process the entities (bspc.c) qboolean nomerge; //don't merge bsp node faces (faces.c) qboolean nowater; //don't use the water brushes (map.c) qboolean nocsg; //don't carve intersecting brushes (bspc.c) qboolean noweld; //use unique face vertexes (faces.c) qboolean noshare; //don't share bsp edges (faces.c) qboolean nosubdiv; //don't subdivide bsp node faces (faces.c) qboolean notjunc; //don't create tjunctions (edge melting) (faces.c) qboolean optimize; //enable optimisation qboolean leaktest; //perform a leak test qboolean verboseentities; qboolean freetree; //free the bsp tree when not needed anymore qboolean create_aas; //create an .AAS file qboolean nobrushmerge; //don't merge brushes qboolean lessbrushes; //create less brushes instead of correct texture placement qboolean cancelconversion; //true if the conversion is being cancelled qboolean noliquids; //no liquids when writing map file qboolean forcesidesvisible; //force all brush sides to be visible when loaded from bsp qboolean writeaasmap; // // qboolean capsule_collision = true; //use capsule collision qboolean capsule_collision = false; // capsule collision// Ridah, allow to specify an extension for multiple AAS files per map char aas_extension[64]; // done. float VectorDistance( vec3_t v1, vec3_t v2 ) { vec3_t dir; VectorSubtract( v2, v1, dir ); return VectorLength( dir ); } /* //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void ProcessWorldModel (void) { entity_t *e; tree_t *tree; qboolean leaked; int brush_start, brush_end; e = &entities[entity_num]; brush_start = e->firstbrush; brush_end = brush_start + e->numbrushes; leaked = false; //process the whole world in one time tree = ProcessWorldBrushes(brush_start, brush_end); //create the bsp tree portals MakeTreePortals(tree); //mark all leafs that can be reached by entities if (FloodEntities(tree)) { FillOutside(tree->headnode); } //end if else { Log_Print("**** leaked ****\n"); leaked = true; LeakFile(tree); if (leaktest) { Log_Print("--- MAP LEAKED ---\n"); exit(0); } //end if } //end else MarkVisibleSides (tree, brush_start, brush_end); FloodAreas (tree); #ifndef ME if (glview) WriteGLView(tree, source); #endif MakeFaces(tree->headnode); FixTjuncs(tree->headnode); //NOTE: Never prune the nodes because the portals // are screwed when prunning is done and as // a result portal writing will crash //if (!noprune) PruneNodes(tree->headnode); WriteBSP(tree->headnode); if (!leaked) WritePortalFile(tree); Tree_Free(tree); } //end of the function ProcessWorldModel //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void ProcessSubModel (void) { entity_t *e; int start, end; tree_t *tree; bspbrush_t *list; vec3_t mins, maxs; e = &entities[entity_num]; start = e->firstbrush; end = start + e->numbrushes; mins[0] = mins[1] = mins[2] = -4096; maxs[0] = maxs[1] = maxs[2] = 4096; list = MakeBspBrushList(start, end, mins, maxs); if (!nocsg) list = ChopBrushes (list); tree = BrushBSP (list, mins, maxs); MakeTreePortals (tree); MarkVisibleSides (tree, start, end); MakeFaces (tree->headnode); FixTjuncs (tree->headnode); WriteBSP (tree->headnode); Tree_Free (tree); } //end of the function ProcessSubModel //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void ProcessModels (void) { BeginBSPFile(); for (entity_num = 0; entity_num < num_entities; entity_num++) { if (!entities[entity_num].numbrushes) continue; Log_Print("############### model %i ###############\n", nummodels); BeginModel(); if (entity_num == 0) ProcessWorldModel(); else ProcessSubModel(); EndModel(); if (!verboseentities) verbose = false; // don't bother printing submodels } //end for EndBSPFile(); } //end of the function ProcessModels //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void Win_Map2Bsp(char *bspfilename) { double start, end; char path[1024]; start = I_FloatTime(); ThreadSetDefault(); //yeah sure Carmack //numthreads = 1; // multiple threads aren't helping... strcpy(source, ExpandArg(bspfilename)); StripExtension(source); //delete portal and line files sprintf(path, "%s.prt", source); remove(path); sprintf(path, "%s.lin", source); remove(path); strcpy(name, ExpandArg(bspfilename)); DefaultExtension(name, ".map"); // might be .reg Q2_AllocMaxBSP(); // SetModelNumbers(); SetLightStyles(); ProcessModels(); //write the BSP Q2_WriteBSPFile(bspfilename); Q2_FreeMaxBSP(); end = I_FloatTime(); Log_Print("%5.0f seconds elapsed\n", end-start); } //end of the function Win_Map2Bsp //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void Map2Bsp(char *mapfilename, char *outputfilename) { double start, end; char path[1024]; start = I_FloatTime (); ThreadSetDefault (); //yeah sure Carmack //numthreads = 1; //multiple threads aren't helping... //SetQdirFromPath(bspfilename); strcpy(source, ExpandArg(mapfilename)); StripExtension(source); // delete portal and line files sprintf(path, "%s.prt", source); remove(path); sprintf(path, "%s.lin", source); remove(path); strcpy(name, ExpandArg(mapfilename)); DefaultExtension(name, ".map"); // might be .reg // // if onlyents, just grab the entites and resave // if (onlyents) { char out[1024]; Q2_AllocMaxBSP(); sprintf (out, "%s.bsp", source); Q2_LoadBSPFile(out, 0, 0); num_entities = 0; Q2_LoadMapFile(name); SetModelNumbers(); SetLightStyles(); Q2_UnparseEntities(); Q2_WriteBSPFile(out); // Q2_FreeMaxBSP(); } //end if else { // // start from scratch // Q2_AllocMaxBSP(); //load the map Q2_LoadMapFile(name); //create the .bsp file SetModelNumbers(); SetLightStyles(); ProcessModels(); //write the BSP Q2_WriteBSPFile(outputfilename); // Q2_FreeMaxBSP(); } //end else end = I_FloatTime(); Log_Print("%5.0f seconds elapsed\n", end-start); } //end of the function Map2Bsp */ //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AASOuputFile( quakefile_t *qf, char *outputpath, char *filename ) { char ext[MAX_PATH]; // if ( strlen( outputpath ) ) { strcpy( filename, outputpath ); //append the bsp file base AppendPathSeperator( filename, MAX_PATH ); ExtractFileBase( qf->origname, &filename[strlen( filename )] ); // Ridah, add extension strcat( filename, aas_extension ); // done. //append .aas strcat( filename, ".aas" ); return; } //end if // ExtractFileExtension( qf->filename, ext ); if ( !stricmp( ext, "pk3" ) || !stricmp( ext, "pak" ) || !stricmp( ext, "sin" ) ) { strcpy( filename, qf->filename ); while ( strlen( filename ) && filename[strlen( filename ) - 1] != '\\' && filename[strlen( filename ) - 1] != '/' ) { filename[strlen( filename ) - 1] = '\0'; } //end while strcat( filename, "maps" ); if ( access( filename, 0x04 ) ) { CreatePath( filename ); } //append the bsp file base AppendPathSeperator( filename, MAX_PATH ); ExtractFileBase( qf->origname, &filename[strlen( filename )] ); // Ridah, add extension strcat( filename, aas_extension ); // done. //append .aas strcat( filename, ".aas" ); } //end if else { strcpy( filename, qf->filename ); while ( strlen( filename ) && filename[strlen( filename ) - 1] != '.' ) { filename[strlen( filename ) - 1] = '\0'; } //end while // Ridah, add extension strcat( filename, aas_extension ); // done. strcat( filename, "aas" ); } //end else } //end of the function AASOutputFile //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void CreateAASFilesForAllBSPFiles( char *quakepath ) { #if defined( WIN32 ) | defined( _WIN32 ) WIN32_FIND_DATA filedata; HWND handle; struct _stat statbuf; #else glob_t globbuf; struct stat statbuf; int j; #endif //int done; // TTimo: unused char filter[_MAX_PATH], bspfilter[_MAX_PATH], aasfilter[_MAX_PATH]; char aasfile[_MAX_PATH], buf[_MAX_PATH], foldername[_MAX_PATH]; quakefile_t *qf, *qf2, *files, *bspfiles, *aasfiles; strcpy( filter, quakepath ); AppendPathSeperator( filter, sizeof( filter ) ); strcat( filter, "*" ); #if defined( WIN32 ) | defined( _WIN32 ) handle = FindFirstFile( filter, &filedata ); done = ( handle == INVALID_HANDLE_VALUE ); while ( !done ) { _splitpath( filter, foldername, NULL, NULL, NULL ); _splitpath( filter, NULL, &foldername[strlen( foldername )], NULL, NULL ); AppendPathSeperator( foldername, _MAX_PATH ); strcat( foldername, filedata.cFileName ); _stat( foldername, &statbuf ); #else glob( filter, 0, NULL, &globbuf ); for ( j = 0; j < globbuf.gl_pathc; j++ ) { strcpy( foldername, globbuf.gl_pathv[j] ); stat( foldername, &statbuf ); #endif //if it is a folder if ( statbuf.st_mode & S_IFDIR ) { // AppendPathSeperator( foldername, sizeof( foldername ) ); //get all the bsp files strcpy( bspfilter, foldername ); strcat( bspfilter, "maps/*.bsp" ); files = FindQuakeFiles( bspfilter ); strcpy( bspfilter, foldername ); strcat( bspfilter, "*.pk3/maps/*.bsp" ); bspfiles = FindQuakeFiles( bspfilter ); for ( qf = bspfiles; qf; qf = qf->next ) if ( !qf->next ) { break; } if ( qf ) { qf->next = files; } else { bspfiles = files;} //get all the aas files strcpy( aasfilter, foldername ); strcat( aasfilter, "maps/*.aas" ); files = FindQuakeFiles( aasfilter ); strcpy( aasfilter, foldername ); strcat( aasfilter, "*.pk3/maps/*.aas" ); aasfiles = FindQuakeFiles( aasfilter ); for ( qf = aasfiles; qf; qf = qf->next ) if ( !qf->next ) { break; } if ( qf ) { qf->next = files; } else { aasfiles = files;} // for ( qf = bspfiles; qf; qf = qf->next ) { sprintf( aasfile, "%s/%s", qf->pakfile, qf->origname ); Log_Print( "found %s\n", aasfile ); strcpy( &aasfile[strlen( aasfile ) - strlen( ".bsp" )], ".aas" ); for ( qf2 = aasfiles; qf2; qf2 = qf2->next ) { sprintf( buf, "%s/%s", qf2->pakfile, qf2->origname ); if ( !stricmp( aasfile, buf ) ) { Log_Print( "found %s\n", buf ); break; } //end if } //end for } //end for } //end if #if defined( WIN32 ) | defined( _WIN32 ) //find the next file done = !FindNextFile( handle, &filedata ); } //end while #else } //end for globfree( &globbuf ); #endif } //end of the function CreateAASFilesForAllBSPFiles //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== quakefile_t *GetArgumentFiles( int argc, char *argv[], int *i, char *ext ) { quakefile_t *qfiles, *lastqf, *qf; int j; char buf[1024]; qfiles = NULL; lastqf = NULL; for (; ( *i ) + 1 < argc && argv[( *i ) + 1][0] != '-'; ( *i )++ ) { strcpy( buf, argv[( *i ) + 1] ); for ( j = strlen( buf ) - 1; j >= strlen( buf ) - 4; j-- ) if ( buf[j] == '.' ) { break; } if ( j >= strlen( buf ) - 4 ) { strcpy( &buf[j + 1], ext ); } qf = FindQuakeFiles( buf ); if ( !qf ) { continue; } if ( lastqf ) { lastqf->next = qf; } else { qfiles = qf;} lastqf = qf; while ( lastqf->next ) lastqf = lastqf->next; } //end for return qfiles; } //end of the function GetArgumentFiles //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== #define COMP_BSP2MAP 1 #define COMP_BSP2AAS 2 #define COMP_REACH 3 #define COMP_CLUSTER 4 #define COMP_AASOPTIMIZE 5 #define COMP_AASINFO 6 #define COMP_TETRA 7 //#define GTKRADIANT #ifdef GTKRADIANT // TTimo: comment this out and use a bogus RADIANT_VERSION if necessary #include "include/version.h" #endif int main( int argc, char **argv ) { int i, comp = 0; char outputpath[MAX_PATH] = ""; char filename[MAX_PATH] = "unknown"; quakefile_t *qfiles = NULL, *qf; // TTimo: init // Ridah, allow to specify an extension for multiple AAS files per map int has_ext = 0; // done. // Ridah, set the world pointer up for reachabilities aasworld = aasworlds; AAS_SetWorldPointer( &( *aasworld ) ); // done. myargc = argc; myargv = argv; Log_Open( "bspc.log" ); //open a log file #ifdef GTKRADIANT Log_Print( "BSPC version " BSPC_VERSION " by Mr Elusive\n" ); Log_Print( "GtkRadiant " RADIANT_VERSION " %s\n", __DATE__ ); #else Log_Print( "BSPC version " BSPC_VERSION ", %s %s by Mr Elusive\n", __DATE__, __TIME__ ); #endif DefaultCfg(); for ( i = 1; i < argc; i++ ) { if ( !stricmp( argv[i],"-threads" ) ) { if ( i + 1 >= argc ) { i = 0; break; } numthreads = atoi( argv[++i] ); Log_Print( "threads = %d\n", numthreads ); } //end if else if ( !stricmp( argv[i], "-noverbose" ) ) { Log_Print( "verbose = false\n" ); verbose = false; } //end else if else if ( !stricmp( argv[i], "-nocsg" ) ) { Log_Print( "nocsg = true\n" ); nocsg = true; } //end else if else if ( !stricmp( argv[i], "-optimize" ) ) { Log_Print( "optimize = true\n" ); optimize = true; } //end else if /* else if (!stricmp(argv[i],"-glview")) { glview = true; } //end else if else if (!stricmp(argv[i], "-draw")) { Log_Print("drawflag = true\n"); drawflag = true; } //end else if else if (!stricmp(argv[i], "-noweld")) { Log_Print("noweld = true\n"); noweld = true; } //end else if else if (!stricmp(argv[i], "-noshare")) { Log_Print("noshare = true\n"); noshare = true; } //end else if else if (!stricmp(argv[i], "-notjunc")) { Log_Print("notjunc = true\n"); notjunc = true; } //end else if else if (!stricmp(argv[i], "-nowater")) { Log_Print("nowater = true\n"); nowater = true; } //end else if else if (!stricmp(argv[i], "-noprune")) { Log_Print("noprune = true\n"); noprune = true; } //end else if else if (!stricmp(argv[i], "-nomerge")) { Log_Print("nomerge = true\n"); nomerge = true; } //end else if else if (!stricmp(argv[i], "-nosubdiv")) { Log_Print("nosubdiv = true\n"); nosubdiv = true; } //end else if else if (!stricmp(argv[i], "-nodetail")) { Log_Print("nodetail = true\n"); nodetail = true; } //end else if else if (!stricmp(argv[i], "-fulldetail")) { Log_Print("fulldetail = true\n"); fulldetail = true; } //end else if else if (!stricmp(argv[i], "-onlyents")) { Log_Print("onlyents = true\n"); onlyents = true; } //end else if else if (!stricmp(argv[i], "-micro")) { if (i + 1 >= argc) {i = 0; break;} microvolume = atof(argv[++i]); Log_Print("microvolume = %f\n", microvolume); } //end else if else if (!stricmp(argv[i], "-leaktest")) { Log_Print("leaktest = true\n"); leaktest = true; } //end else if else if (!stricmp(argv[i], "-verboseentities")) { Log_Print("verboseentities = true\n"); verboseentities = true; } //end else if else if (!stricmp(argv[i], "-chop")) { if (i + 1 >= argc) {i = 0; break;} subdivide_size = atof(argv[++i]); Log_Print("subdivide_size = %f\n", subdivide_size); } //end else if else if (!stricmp (argv[i], "-tmpout")) { strcpy (outbase, "/tmp"); Log_Print("temp output\n"); } //end else if */ #ifdef ME else if ( !stricmp( argv[i], "-freetree" ) ) { freetree = true; Log_Print( "freetree = true\n" ); } //end else if else if ( !stricmp( argv[i], "-grapplereach" ) ) { calcgrapplereach = true; Log_Print( "grapplereach = true\n" ); } //end else if else if ( !stricmp( argv[i], "-nobrushmerge" ) ) { nobrushmerge = true; Log_Print( "nobrushmerge = true\n" ); } //end else if else if ( !stricmp( argv[i], "-noliquids" ) ) { noliquids = true; Log_Print( "noliquids = true\n" ); } //end else if else if ( !stricmp( argv[i], "-forcesidesvisible" ) ) { forcesidesvisible = true; Log_Print( "forcesidesvisible = true\n" ); } //end else if else if ( !stricmp( argv[i], "-output" ) ) { if ( i + 1 >= argc ) { i = 0; break; } if ( access( argv[i + 1], 0x04 ) ) { Warning( "the folder %s does not exist", argv[i + 1] ); } strcpy( outputpath, argv[++i] ); } //end else if else if ( !stricmp( argv[i], "-breadthfirst" ) ) { use_nodequeue = true; Log_Print( "breadthfirst = true\n" ); } //end else if else if ( !stricmp( argv[i], "-cfg" ) ) { if ( i + 1 >= argc ) { i = 0; break; } if ( !LoadCfgFile( argv[++i] ) ) { exit( 0 ); } } //end else if else if ( !stricmp( argv[i], "-bsp2map" ) ) { if ( i + 1 >= argc ) { i = 0; break; } comp = COMP_BSP2MAP; qfiles = GetArgumentFiles( argc, argv, &i, "bsp" ); } //end else if else if ( !stricmp( argv[i], "-bsp2aas" ) ) { if ( i + 1 >= argc ) { i = 0; break; } comp = COMP_BSP2AAS; qfiles = GetArgumentFiles( argc, argv, &i, "bsp" ); } //end else if else if ( !stricmp( argv[i], "-aasall" ) ) { if ( i + 1 >= argc ) { i = 0; break; } CreateAASFilesForAllBSPFiles( argv[++i] ); } //end else if else if ( !stricmp( argv[i], "-reach" ) ) { if ( i + 1 >= argc ) { i = 0; break; } comp = COMP_REACH; qfiles = GetArgumentFiles( argc, argv, &i, "bsp" ); } //end else if else if ( !stricmp( argv[i], "-cluster" ) ) { if ( i + 1 >= argc ) { i = 0; break; } comp = COMP_CLUSTER; qfiles = GetArgumentFiles( argc, argv, &i, "bsp" ); } //end else if else if ( !stricmp( argv[i], "-aasinfo" ) ) { if ( i + 1 >= argc ) { i = 0; break; } comp = COMP_AASINFO; qfiles = GetArgumentFiles( argc, argv, &i, "aas" ); } //end else if else if ( !stricmp( argv[i], "-aasopt" ) ) { if ( i + 1 >= argc ) { i = 0; break; } comp = COMP_AASOPTIMIZE; qfiles = GetArgumentFiles( argc, argv, &i, "aas" ); } //end else if else if ( !stricmp( argv[i], "-tetra" ) ) { if ( i + 1 >= argc ) { i = 0; break; } comp = COMP_TETRA; qfiles = GetArgumentFiles( argc, argv, &i, "aas" ); } //end else if else if ( !stricmp( argv[i], "-writeaasmap" ) ) { writeaasmap = true; Log_Print( "writeaasmap = true\n" ); } // Ridah, allow to specify an extension for multiple AAS files per map else if ( !stricmp( argv[i], "-ext" ) ) { if ( i + 1 >= argc ) { i = 0; break; } strcpy( aas_extension, argv[++i] ); has_ext = 1; } //end else if // done. #endif //ME else { Log_Print( "unknown parameter %s\n", argv[i] ); break; } //end else } //end for //if there are parameters and there's no mismatch in one of the parameters if ( argc > 1 && i == argc ) { switch ( comp ) { case COMP_BSP2MAP: { if ( !qfiles ) { Log_Print( "no files found\n" ); } for ( qf = qfiles; qf; qf = qf->next ) { //copy the output path strcpy( filename, outputpath ); //append the bsp file base AppendPathSeperator( filename, MAX_PATH ); ExtractFileBase( qf->origname, &filename[strlen( filename )] ); //append .map strcat( filename, ".map" ); // Log_Print( "bsp2map: %s to %s\n", qf->origname, filename ); if ( qf->type != QFILETYPE_BSP ) { Warning( "%s is probably not a BSP file\n", qf->origname ); } // LoadMapFromBSP( qf ); //write the map file WriteMapFile( filename ); } //end for break; } //end case case COMP_BSP2AAS: { if ( !qfiles ) { Log_Print( "no files found\n" ); } for ( qf = qfiles; qf; qf = qf->next ) { AASOuputFile( qf, outputpath, filename ); // Log_Print( "bsp2aas: %s to %s\n", qf->origname, filename ); if ( qf->type != QFILETYPE_BSP ) { Warning( "%s is probably not a BSP file\n", qf->origname ); } //set before map loading create_aas = 1; LoadMapFromBSP( qf ); //create the AAS file AAS_Create( filename ); //if it's a Quake3 map calculate the reachabilities and clusters if ( loadedmaptype == MAPTYPE_QUAKE3 ) { AAS_CalcReachAndClusters( qf ); } // if ( optimize ) { AAS_Optimize(); } // //write out the stored AAS file if ( !AAS_WriteAASFile( filename ) ) { Error( "error writing %s\n", filename ); } //end if //deallocate memory AAS_FreeMaxAAS(); } //end for break; } //end case case COMP_REACH: { if ( !qfiles ) { Log_Print( "no files found\n" ); } for ( qf = qfiles; qf; qf = qf->next ) { AASOuputFile( qf, outputpath, filename ); // Log_Print( "reach: %s to %s\n", qf->origname, filename ); if ( qf->type != QFILETYPE_BSP ) { Warning( "%s is probably not a BSP file\n", qf->origname ); } //if the AAS file exists in the output directory if ( !access( filename, 0x04 ) ) { if ( !AAS_LoadAASFile( filename, 0, 0 ) ) { Error( "error loading aas file %s\n", filename ); } //end if //assume it's a Quake3 BSP file loadedmaptype = MAPTYPE_QUAKE3; } //end if else { Warning( "AAS file %s not found in output folder\n", filename ); Log_Print( "creating %s...\n", filename ); //set before map loading create_aas = 1; LoadMapFromBSP( qf ); //create the AAS file AAS_Create( filename ); } //end else //if it's a Quake3 map calculate the reachabilities and clusters if ( loadedmaptype == MAPTYPE_QUAKE3 ) { AAS_CalcReachAndClusters( qf ); } //end if // if ( optimize ) { AAS_Optimize(); } //write out the stored AAS file if ( !AAS_WriteAASFile( filename ) ) { Error( "error writing %s\n", filename ); } //end if //deallocate memory AAS_FreeMaxAAS(); } //end for break; } //end case case COMP_CLUSTER: { if ( !qfiles ) { Log_Print( "no files found\n" ); } for ( qf = qfiles; qf; qf = qf->next ) { AASOuputFile( qf, outputpath, filename ); // Log_Print( "cluster: %s to %s\n", qf->origname, filename ); if ( qf->type != QFILETYPE_BSP ) { Warning( "%s is probably not a BSP file\n", qf->origname ); } //if the AAS file exists in the output directory if ( !access( filename, 0x04 ) ) { if ( !AAS_LoadAASFile( filename, 0, 0 ) ) { Error( "error loading aas file %s\n", filename ); } //end if //assume it's a Quake3 BSP file loadedmaptype = MAPTYPE_QUAKE3; //if it's a Quake3 map calculate the clusters if ( loadedmaptype == MAPTYPE_QUAKE3 ) { ( *aasworld ).numclusters = 0; AAS_InitBotImport(); AAS_InitClustering(); } //end if } //end if else { Warning( "AAS file %s not found in output folder\n", filename ); Log_Print( "creating %s...\n", filename ); //set before map loading create_aas = 1; LoadMapFromBSP( qf ); //create the AAS file AAS_Create( filename ); //if it's a Quake3 map calculate the reachabilities and clusters if ( loadedmaptype == MAPTYPE_QUAKE3 ) { AAS_CalcReachAndClusters( qf ); } } //end else // if ( optimize ) { AAS_Optimize(); } //write out the stored AAS file if ( !AAS_WriteAASFile( filename ) ) { Error( "error writing %s\n", filename ); } //end if //deallocate memory AAS_FreeMaxAAS(); } //end for break; } //end case case COMP_AASOPTIMIZE: { if ( !qfiles ) { Log_Print( "no files found\n" ); } for ( qf = qfiles; qf; qf = qf->next ) { AASOuputFile( qf, outputpath, filename ); // Log_Print( "optimizing: %s to %s\n", qf->origname, filename ); if ( qf->type != QFILETYPE_AAS ) { Warning( "%s is probably not a AAS file\n", qf->origname ); } // AAS_InitBotImport(); // if ( !AAS_LoadAASFile( qf->filename, qf->offset, qf->length ) ) { Error( "error loading aas file %s\n", qf->filename ); } //end if AAS_Optimize(); //write out the stored AAS file if ( !AAS_WriteAASFile( filename ) ) { Error( "error writing %s\n", filename ); } //end if //deallocate memory AAS_FreeMaxAAS(); } //end for break; } //end case case COMP_AASINFO: { if ( !qfiles ) { Log_Print( "no files found\n" ); } for ( qf = qfiles; qf; qf = qf->next ) { AASOuputFile( qf, outputpath, filename ); // Log_Print( "aas info for: %s\n", filename ); if ( qf->type != QFILETYPE_AAS ) { Warning( "%s is probably not a AAS file\n", qf->origname ); } // AAS_InitBotImport(); // if ( !AAS_LoadAASFile( qf->filename, qf->offset, qf->length ) ) { Error( "error loading aas file %s\n", qf->filename ); } //end if AAS_ShowTotals(); } //end for } //end case case COMP_TETRA: { if ( !qfiles ) { Log_Print( "no files found\n" ); } for ( qf = qfiles; qf; qf = qf->next ) { //TH_AASToTetrahedrons(qf->filename); } //end for break; } //end case default: { Log_Print( "don't know what to do\n" ); break; } //end default } //end switch } //end if else { Log_Print( "Usage: bspc [- [- ...]]\n" #if defined( WIN32 ) || defined( _WIN32 ) "Example 1: bspc -bsp2aas d:\\quake3\\baseq3\\maps\\mymap?.bsp\n" "Example 2: bspc -bsp2aas d:\\quake3\\baseq3\\pak0.pk3\\maps/q3dm*.bsp\n" #else "Example 1: bspc -bsp2aas /quake3/baseq3/maps/mymap?.bsp\n" "Example 2: bspc -bsp2aas /quake3/baseq3/pak0.pk3/maps/q3dm*.bsp\n" #endif "\n" "Switches:\n" //" bsp2map <[pakfilter/]filter.bsp> = convert BSP to MAP\n" " bsp2aas <[pakfilter/]filter.bsp> = convert BSP to AAS\n" //" aasall = create AAS files for all BSPs\n" " reach = compute reachability & clusters\n" " cluster = compute clusters\n" " aasopt = optimize aas file\n" //" tetra = tetrahedral decomposition\n" " output = set output path\n" " threads = set number of threads to X\n" " cfg = use this cfg file\n" " optimize = enable optimization\n" " noverbose = disable verbose output\n" " breadthfirst = breadth first bsp building\n" " nobrushmerge = don't merge brushes\n" " noliquids = don't write liquids to map\n" " freetree = free the bsp tree\n" " nocsg = disables brush chopping\n" " forcesidesvisible = force all sides to be visible\n" " grapplereach = calculate grapple reachabilities\n" " writeaasmap = write the map the AI sees\n" /* " glview = output a GL view\n" " draw = enables drawing\n" " noweld = disables weld\n" " noshare = disables sharing\n" " notjunc = disables juncs\n" " nowater = disables water brushes\n" " noprune = disables node prunes\n" " nomerge = disables face merging\n" " nosubdiv = disables subdeviding\n" " nodetail = disables detail brushes\n" " fulldetail = enables full detail\n" " onlyents = only compile entities with bsp\n" " micro \n" " = sets the micro volume to the given float\n" " leaktest = perform a leak test\n" " verboseentities\n" " = enable entity verbose mode\n" " chop \n" " = sets the subdivide size to the given float\n"*/ "\n" ); } //end else Log_Close(); //close the log file return 0; } //end of the function main