bspc.c (28389B)
1 /* 2 =========================================================================== 3 Copyright (C) 1999-2005 Id Software, Inc. 4 5 This file is part of Quake III Arena source code. 6 7 Quake III Arena source code is free software; you can redistribute it 8 and/or modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the License, 10 or (at your option) any later version. 11 12 Quake III Arena source code is distributed in the hope that it will be 13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Foobar; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 =========================================================================== 21 */ 22 23 #if defined(WIN32) || defined(_WIN32) 24 #include <direct.h> 25 #include <windows.h> 26 #include <sys/types.h> 27 #include <sys/stat.h> 28 #else 29 #include <unistd.h> 30 #include <glob.h> 31 #include <sys/stat.h> 32 #include <unistd.h> 33 #endif 34 #include "qbsp.h" 35 #include "l_mem.h" 36 #include "../botlib/aasfile.h" 37 #include "../botlib/be_aas_cluster.h" 38 #include "../botlib/be_aas_optimize.h" 39 #include "aas_create.h" 40 #include "aas_store.h" 41 #include "aas_file.h" 42 #include "aas_cfg.h" 43 #include "be_aas_bspc.h" 44 45 extern int use_nodequeue; //brushbsp.c 46 extern int calcgrapplereach; //be_aas_reach.c 47 48 float subdivide_size = 240; 49 char source[1024]; 50 char name[1024]; 51 vec_t microvolume = 1.0; 52 char outbase[32]; 53 int entity_num; 54 aas_settings_t aassettings; 55 56 qboolean noprune; //don't prune nodes (bspc.c) 57 qboolean glview; //create a gl view 58 qboolean nodetail; //don't use detail brushes (map.c) 59 qboolean fulldetail; //use but don't mark detail brushes (map.c) 60 qboolean onlyents; //only process the entities (bspc.c) 61 qboolean nomerge; //don't merge bsp node faces (faces.c) 62 qboolean nowater; //don't use the water brushes (map.c) 63 qboolean nocsg; //don't carve intersecting brushes (bspc.c) 64 qboolean noweld; //use unique face vertexes (faces.c) 65 qboolean noshare; //don't share bsp edges (faces.c) 66 qboolean nosubdiv; //don't subdivide bsp node faces (faces.c) 67 qboolean notjunc; //don't create tjunctions (edge melting) (faces.c) 68 qboolean optimize; //enable optimisation 69 qboolean leaktest; //perform a leak test 70 qboolean verboseentities; 71 qboolean freetree; //free the bsp tree when not needed anymore 72 qboolean create_aas; //create an .AAS file 73 qboolean nobrushmerge; //don't merge brushes 74 qboolean lessbrushes; //create less brushes instead of correct texture placement 75 qboolean cancelconversion; //true if the conversion is being cancelled 76 qboolean noliquids; //no liquids when writing map file 77 qboolean forcesidesvisible; //force all brush sides to be visible when loaded from bsp 78 qboolean capsule_collision = 0; 79 80 /* 81 //=========================================================================== 82 // 83 // Parameter: - 84 // Returns: - 85 // Changes Globals: - 86 //=========================================================================== 87 void ProcessWorldModel (void) 88 { 89 entity_t *e; 90 tree_t *tree; 91 qboolean leaked; 92 int brush_start, brush_end; 93 94 e = &entities[entity_num]; 95 96 brush_start = e->firstbrush; 97 brush_end = brush_start + e->numbrushes; 98 leaked = false; 99 100 //process the whole world in one time 101 tree = ProcessWorldBrushes(brush_start, brush_end); 102 //create the bsp tree portals 103 MakeTreePortals(tree); 104 //mark all leafs that can be reached by entities 105 if (FloodEntities(tree)) 106 { 107 FillOutside(tree->headnode); 108 } //end if 109 else 110 { 111 Log_Print("**** leaked ****\n"); 112 leaked = true; 113 LeakFile(tree); 114 if (leaktest) 115 { 116 Log_Print("--- MAP LEAKED ---\n"); 117 exit(0); 118 } //end if 119 } //end else 120 121 MarkVisibleSides (tree, brush_start, brush_end); 122 123 FloodAreas (tree); 124 125 #ifndef ME 126 if (glview) WriteGLView(tree, source); 127 #endif 128 MakeFaces(tree->headnode); 129 FixTjuncs(tree->headnode); 130 131 //NOTE: Never prune the nodes because the portals 132 // are screwed when prunning is done and as 133 // a result portal writing will crash 134 //if (!noprune) PruneNodes(tree->headnode); 135 136 WriteBSP(tree->headnode); 137 138 if (!leaked) WritePortalFile(tree); 139 140 Tree_Free(tree); 141 } //end of the function ProcessWorldModel 142 //=========================================================================== 143 // 144 // Parameter: - 145 // Returns: - 146 // Changes Globals: - 147 //=========================================================================== 148 void ProcessSubModel (void) 149 { 150 entity_t *e; 151 int start, end; 152 tree_t *tree; 153 bspbrush_t *list; 154 vec3_t mins, maxs; 155 156 e = &entities[entity_num]; 157 158 start = e->firstbrush; 159 end = start + e->numbrushes; 160 161 mins[0] = mins[1] = mins[2] = -4096; 162 maxs[0] = maxs[1] = maxs[2] = 4096; 163 list = MakeBspBrushList(start, end, mins, maxs); 164 if (!nocsg) list = ChopBrushes (list); 165 tree = BrushBSP (list, mins, maxs); 166 MakeTreePortals (tree); 167 MarkVisibleSides (tree, start, end); 168 MakeFaces (tree->headnode); 169 FixTjuncs (tree->headnode); 170 WriteBSP (tree->headnode); 171 Tree_Free(tree); 172 } //end of the function ProcessSubModel 173 //=========================================================================== 174 // 175 // Parameter: - 176 // Returns: - 177 // Changes Globals: - 178 //=========================================================================== 179 void ProcessModels (void) 180 { 181 BeginBSPFile(); 182 183 for (entity_num = 0; entity_num < num_entities; entity_num++) 184 { 185 if (!entities[entity_num].numbrushes) 186 continue; 187 188 Log_Print("############### model %i ###############\n", nummodels); 189 BeginModel(); 190 if (entity_num == 0) ProcessWorldModel(); 191 else ProcessSubModel(); 192 EndModel(); 193 194 if (!verboseentities) 195 verbose = false; // don't bother printing submodels 196 } //end for 197 EndBSPFile(); 198 } //end of the function ProcessModels 199 //=========================================================================== 200 // 201 // Parameter: - 202 // Returns: - 203 // Changes Globals: - 204 //=========================================================================== 205 void Win_Map2Bsp(char *bspfilename) 206 { 207 double start, end; 208 char path[1024]; 209 210 start = I_FloatTime(); 211 212 ThreadSetDefault(); 213 //yeah sure Carmack 214 //numthreads = 1; // multiple threads aren't helping... 215 216 strcpy(source, ExpandArg(bspfilename)); 217 StripExtension(source); 218 219 //delete portal and line files 220 sprintf(path, "%s.prt", source); 221 remove(path); 222 sprintf(path, "%s.lin", source); 223 remove(path); 224 225 strcpy(name, ExpandArg(bspfilename)); 226 DefaultExtension(name, ".map"); // might be .reg 227 228 Q2_AllocMaxBSP(); 229 // 230 SetModelNumbers(); 231 SetLightStyles(); 232 ProcessModels(); 233 //write the BSP 234 Q2_WriteBSPFile(bspfilename); 235 236 Q2_FreeMaxBSP(); 237 238 end = I_FloatTime(); 239 Log_Print("%5.0f seconds elapsed\n", end-start); 240 } //end of the function Win_Map2Bsp 241 //=========================================================================== 242 // 243 // Parameter: - 244 // Returns: - 245 // Changes Globals: - 246 //=========================================================================== 247 void Map2Bsp(char *mapfilename, char *outputfilename) 248 { 249 double start, end; 250 char path[1024]; 251 252 start = I_FloatTime (); 253 254 ThreadSetDefault (); 255 //yeah sure Carmack 256 //numthreads = 1; //multiple threads aren't helping... 257 //SetQdirFromPath(bspfilename); 258 259 strcpy(source, ExpandArg(mapfilename)); 260 StripExtension(source); 261 262 // delete portal and line files 263 sprintf(path, "%s.prt", source); 264 remove(path); 265 sprintf(path, "%s.lin", source); 266 remove(path); 267 268 strcpy(name, ExpandArg(mapfilename)); 269 DefaultExtension(name, ".map"); // might be .reg 270 271 // 272 // if onlyents, just grab the entites and resave 273 // 274 if (onlyents) 275 { 276 char out[1024]; 277 278 Q2_AllocMaxBSP(); 279 sprintf (out, "%s.bsp", source); 280 Q2_LoadBSPFile(out, 0, 0); 281 num_entities = 0; 282 283 Q2_LoadMapFile(name); 284 SetModelNumbers(); 285 SetLightStyles(); 286 287 Q2_UnparseEntities(); 288 289 Q2_WriteBSPFile(out); 290 // 291 Q2_FreeMaxBSP(); 292 } //end if 293 else 294 { 295 // 296 // start from scratch 297 // 298 Q2_AllocMaxBSP(); 299 //load the map 300 Q2_LoadMapFile(name); 301 //create the .bsp file 302 SetModelNumbers(); 303 SetLightStyles(); 304 ProcessModels(); 305 //write the BSP 306 Q2_WriteBSPFile(outputfilename); 307 // 308 Q2_FreeMaxBSP(); 309 } //end else 310 311 end = I_FloatTime(); 312 Log_Print("%5.0f seconds elapsed\n", end-start); 313 } //end of the function Map2Bsp 314 */ 315 //=========================================================================== 316 // 317 // Parameter: - 318 // Returns: - 319 // Changes Globals: - 320 //=========================================================================== 321 void AASOuputFile(quakefile_t *qf, char *outputpath, char *filename) 322 { 323 char ext[MAX_PATH]; 324 325 // 326 if (strlen(outputpath)) 327 { 328 strcpy(filename, outputpath); 329 //append the bsp file base 330 AppendPathSeperator(filename, MAX_PATH); 331 ExtractFileBase(qf->origname, &filename[strlen(filename)]); 332 //append .aas 333 strcat(filename, ".aas"); 334 return; 335 } //end if 336 // 337 ExtractFileExtension(qf->filename, ext); 338 if (!stricmp(ext, "pk3") || !stricmp(ext, "pak") || !stricmp(ext, "sin")) 339 { 340 strcpy(filename, qf->filename); 341 while(strlen(filename) && 342 filename[strlen(filename)-1] != '\\' && 343 filename[strlen(filename)-1] != '/') 344 { 345 filename[strlen(filename)-1] = '\0'; 346 } //end while 347 strcat(filename, "maps"); 348 if (access(filename, 0x04)) CreatePath(filename); 349 //append the bsp file base 350 AppendPathSeperator(filename, MAX_PATH); 351 ExtractFileBase(qf->origname, &filename[strlen(filename)]); 352 //append .aas 353 strcat(filename, ".aas"); 354 } //end if 355 else 356 { 357 strcpy(filename, qf->filename); 358 while(strlen(filename) && 359 filename[strlen(filename)-1] != '.') 360 { 361 filename[strlen(filename)-1] = '\0'; 362 } //end while 363 strcat(filename, "aas"); 364 } //end else 365 } //end of the function AASOutputFile 366 //=========================================================================== 367 // 368 // Parameter: - 369 // Returns: - 370 // Changes Globals: - 371 //=========================================================================== 372 void CreateAASFilesForAllBSPFiles(char *quakepath) 373 { 374 #if defined(WIN32)|defined(_WIN32) 375 WIN32_FIND_DATA filedata; 376 HWND handle; 377 struct _stat statbuf; 378 #else 379 glob_t globbuf; 380 struct stat statbuf; 381 int j; 382 #endif 383 int done; 384 char filter[_MAX_PATH], bspfilter[_MAX_PATH], aasfilter[_MAX_PATH]; 385 char aasfile[_MAX_PATH], buf[_MAX_PATH], foldername[_MAX_PATH]; 386 quakefile_t *qf, *qf2, *files, *bspfiles, *aasfiles; 387 388 strcpy(filter, quakepath); 389 AppendPathSeperator(filter, sizeof(filter)); 390 strcat(filter, "*"); 391 392 #if defined(WIN32)|defined(_WIN32) 393 handle = FindFirstFile(filter, &filedata); 394 done = (handle == INVALID_HANDLE_VALUE); 395 while(!done) 396 { 397 _splitpath(filter, foldername, NULL, NULL, NULL); 398 _splitpath(filter, NULL, &foldername[strlen(foldername)], NULL, NULL); 399 AppendPathSeperator(foldername, _MAX_PATH); 400 strcat(foldername, filedata.cFileName); 401 _stat(foldername, &statbuf); 402 #else 403 glob(filter, 0, NULL, &globbuf); 404 for (j = 0; j < globbuf.gl_pathc; j++) 405 { 406 strcpy(foldername, globbuf.gl_pathv[j]); 407 stat(foldername, &statbuf); 408 #endif 409 //if it is a folder 410 if (statbuf.st_mode & S_IFDIR) 411 { 412 // 413 AppendPathSeperator(foldername, sizeof(foldername)); 414 //get all the bsp files 415 strcpy(bspfilter, foldername); 416 strcat(bspfilter, "maps/*.bsp"); 417 files = FindQuakeFiles(bspfilter); 418 strcpy(bspfilter, foldername); 419 strcat(bspfilter, "*.pk3/maps/*.bsp"); 420 bspfiles = FindQuakeFiles(bspfilter); 421 for (qf = bspfiles; qf; qf = qf->next) if (!qf->next) break; 422 if (qf) qf->next = files; 423 else bspfiles = files; 424 //get all the aas files 425 strcpy(aasfilter, foldername); 426 strcat(aasfilter, "maps/*.aas"); 427 files = FindQuakeFiles(aasfilter); 428 strcpy(aasfilter, foldername); 429 strcat(aasfilter, "*.pk3/maps/*.aas"); 430 aasfiles = FindQuakeFiles(aasfilter); 431 for (qf = aasfiles; qf; qf = qf->next) if (!qf->next) break; 432 if (qf) qf->next = files; 433 else aasfiles = files; 434 // 435 for (qf = bspfiles; qf; qf = qf->next) 436 { 437 sprintf(aasfile, "%s/%s", qf->pakfile, qf->origname); 438 Log_Print("found %s\n", aasfile); 439 strcpy(&aasfile[strlen(aasfile)-strlen(".bsp")], ".aas"); 440 for (qf2 = aasfiles; qf2; qf2 = qf2->next) 441 { 442 sprintf(buf, "%s/%s", qf2->pakfile, qf2->origname); 443 if (!stricmp(aasfile, buf)) 444 { 445 Log_Print("found %s\n", buf); 446 break; 447 } //end if 448 } //end for 449 } //end for 450 } //end if 451 #if defined(WIN32)|defined(_WIN32) 452 //find the next file 453 done = !FindNextFile(handle, &filedata); 454 } //end while 455 #else 456 } //end for 457 globfree(&globbuf); 458 #endif 459 } //end of the function CreateAASFilesForAllBSPFiles 460 //=========================================================================== 461 // 462 // Parameter: - 463 // Returns: - 464 // Changes Globals: - 465 //=========================================================================== 466 quakefile_t *GetArgumentFiles(int argc, char *argv[], int *i, char *ext) 467 { 468 quakefile_t *qfiles, *lastqf, *qf; 469 int j; 470 char buf[1024]; 471 472 qfiles = NULL; 473 lastqf = NULL; 474 for (; (*i)+1 < argc && argv[(*i)+1][0] != '-'; (*i)++) 475 { 476 strcpy(buf, argv[(*i)+1]); 477 for (j = strlen(buf)-1; j >= strlen(buf)-4; j--) 478 if (buf[j] == '.') break; 479 if (j >= strlen(buf)-4) 480 strcpy(&buf[j+1], ext); 481 qf = FindQuakeFiles(buf); 482 if (!qf) continue; 483 if (lastqf) lastqf->next = qf; 484 else qfiles = qf; 485 lastqf = qf; 486 while(lastqf->next) lastqf = lastqf->next; 487 } //end for 488 return qfiles; 489 } //end of the function GetArgumentFiles 490 //=========================================================================== 491 // 492 // Parameter: - 493 // Returns: - 494 // Changes Globals: - 495 //=========================================================================== 496 497 #define COMP_BSP2MAP 1 498 #define COMP_BSP2AAS 2 499 #define COMP_REACH 3 500 #define COMP_CLUSTER 4 501 #define COMP_AASOPTIMIZE 5 502 #define COMP_AASINFO 6 503 504 int main (int argc, char **argv) 505 { 506 int i, comp = 0; 507 char outputpath[MAX_PATH] = ""; 508 char filename[MAX_PATH] = "unknown"; 509 quakefile_t *qfiles, *qf; 510 double start_time; 511 512 myargc = argc; 513 myargv = argv; 514 515 start_time = I_FloatTime(); 516 517 Log_Open("bspc.log"); //open a log file 518 Log_Print("BSPC version "BSPC_VERSION", %s %s\n", __DATE__, __TIME__); 519 520 DefaultCfg(); 521 for (i = 1; i < argc; i++) 522 { 523 if (!stricmp(argv[i],"-threads")) 524 { 525 if (i + 1 >= argc) {i = 0; break;} 526 numthreads = atoi(argv[++i]); 527 Log_Print("threads = %d\n", numthreads); 528 } //end if 529 else if (!stricmp(argv[i], "-noverbose")) 530 { 531 Log_Print("verbose = false\n"); 532 verbose = false; 533 } //end else if 534 else if (!stricmp(argv[i], "-nocsg")) 535 { 536 Log_Print("nocsg = true\n"); 537 nocsg = true; 538 } //end else if 539 else if (!stricmp(argv[i], "-optimize")) 540 { 541 Log_Print("optimize = true\n"); 542 optimize = true; 543 } //end else if 544 /* 545 else if (!stricmp(argv[i],"-glview")) 546 { 547 glview = true; 548 } //end else if 549 else if (!stricmp(argv[i], "-draw")) 550 { 551 Log_Print("drawflag = true\n"); 552 drawflag = true; 553 } //end else if 554 else if (!stricmp(argv[i], "-noweld")) 555 { 556 Log_Print("noweld = true\n"); 557 noweld = true; 558 } //end else if 559 else if (!stricmp(argv[i], "-noshare")) 560 { 561 Log_Print("noshare = true\n"); 562 noshare = true; 563 } //end else if 564 else if (!stricmp(argv[i], "-notjunc")) 565 { 566 Log_Print("notjunc = true\n"); 567 notjunc = true; 568 } //end else if 569 else if (!stricmp(argv[i], "-nowater")) 570 { 571 Log_Print("nowater = true\n"); 572 nowater = true; 573 } //end else if 574 else if (!stricmp(argv[i], "-noprune")) 575 { 576 Log_Print("noprune = true\n"); 577 noprune = true; 578 } //end else if 579 else if (!stricmp(argv[i], "-nomerge")) 580 { 581 Log_Print("nomerge = true\n"); 582 nomerge = true; 583 } //end else if 584 else if (!stricmp(argv[i], "-nosubdiv")) 585 { 586 Log_Print("nosubdiv = true\n"); 587 nosubdiv = true; 588 } //end else if 589 else if (!stricmp(argv[i], "-nodetail")) 590 { 591 Log_Print("nodetail = true\n"); 592 nodetail = true; 593 } //end else if 594 else if (!stricmp(argv[i], "-fulldetail")) 595 { 596 Log_Print("fulldetail = true\n"); 597 fulldetail = true; 598 } //end else if 599 else if (!stricmp(argv[i], "-onlyents")) 600 { 601 Log_Print("onlyents = true\n"); 602 onlyents = true; 603 } //end else if 604 else if (!stricmp(argv[i], "-micro")) 605 { 606 if (i + 1 >= argc) {i = 0; break;} 607 microvolume = atof(argv[++i]); 608 Log_Print("microvolume = %f\n", microvolume); 609 } //end else if 610 else if (!stricmp(argv[i], "-leaktest")) 611 { 612 Log_Print("leaktest = true\n"); 613 leaktest = true; 614 } //end else if 615 else if (!stricmp(argv[i], "-verboseentities")) 616 { 617 Log_Print("verboseentities = true\n"); 618 verboseentities = true; 619 } //end else if 620 else if (!stricmp(argv[i], "-chop")) 621 { 622 if (i + 1 >= argc) {i = 0; break;} 623 subdivide_size = atof(argv[++i]); 624 Log_Print("subdivide_size = %f\n", subdivide_size); 625 } //end else if 626 else if (!stricmp (argv[i], "-tmpout")) 627 { 628 strcpy (outbase, "/tmp"); 629 Log_Print("temp output\n"); 630 } //end else if 631 */ 632 #ifdef ME 633 else if (!stricmp(argv[i], "-freetree")) 634 { 635 freetree = true; 636 Log_Print("freetree = true\n"); 637 } //end else if 638 else if (!stricmp(argv[i], "-grapplereach")) 639 { 640 calcgrapplereach = true; 641 Log_Print("grapplereach = true\n"); 642 } //end else if 643 else if (!stricmp(argv[i], "-nobrushmerge")) 644 { 645 nobrushmerge = true; 646 Log_Print("nobrushmerge = true\n"); 647 } //end else if 648 else if (!stricmp(argv[i], "-noliquids")) 649 { 650 noliquids = true; 651 Log_Print("noliquids = true\n"); 652 } //end else if 653 else if (!stricmp(argv[i], "-forcesidesvisible")) 654 { 655 forcesidesvisible = true; 656 Log_Print("forcesidesvisible = true\n"); 657 } //end else if 658 else if (!stricmp(argv[i], "-output")) 659 { 660 if (i + 1 >= argc) {i = 0; break;} 661 if (access(argv[i+1], 0x04)) Warning("the folder %s does not exist", argv[i+1]); 662 strcpy(outputpath, argv[++i]); 663 } //end else if 664 else if (!stricmp(argv[i], "-breadthfirst")) 665 { 666 use_nodequeue = true; 667 Log_Print("breadthfirst = true\n"); 668 } //end else if 669 else if (!stricmp(argv[i], "-capsule")) 670 { 671 capsule_collision = true; 672 Log_Print("capsule_collision = true\n"); 673 } //end else if 674 else if (!stricmp(argv[i], "-cfg")) 675 { 676 if (i + 1 >= argc) {i = 0; break;} 677 if (!LoadCfgFile(argv[++i])) 678 exit(0); 679 } //end else if 680 else if (!stricmp(argv[i], "-bsp2map")) 681 { 682 if (i + 1 >= argc) {i = 0; break;} 683 comp = COMP_BSP2MAP; 684 qfiles = GetArgumentFiles(argc, argv, &i, "bsp"); 685 } //end else if 686 else if (!stricmp(argv[i], "-bsp2aas")) 687 { 688 if (i + 1 >= argc) {i = 0; break;} 689 comp = COMP_BSP2AAS; 690 qfiles = GetArgumentFiles(argc, argv, &i, "bsp"); 691 } //end else if 692 else if (!stricmp(argv[i], "-aasall")) 693 { 694 if (i + 1 >= argc) {i = 0; break;} 695 CreateAASFilesForAllBSPFiles(argv[++i]); 696 } //end else if 697 else if (!stricmp(argv[i], "-reach")) 698 { 699 if (i + 1 >= argc) {i = 0; break;} 700 comp = COMP_REACH; 701 qfiles = GetArgumentFiles(argc, argv, &i, "bsp"); 702 } //end else if 703 else if (!stricmp(argv[i], "-cluster")) 704 { 705 if (i + 1 >= argc) {i = 0; break;} 706 comp = COMP_CLUSTER; 707 qfiles = GetArgumentFiles(argc, argv, &i, "bsp"); 708 } //end else if 709 else if (!stricmp(argv[i], "-aasinfo")) 710 { 711 if (i + 1 >= argc) {i = 0; break;} 712 comp = COMP_AASINFO; 713 qfiles = GetArgumentFiles(argc, argv, &i, "aas"); 714 } //end else if 715 else if (!stricmp(argv[i], "-aasopt")) 716 { 717 if (i + 1 >= argc) {i = 0; break;} 718 comp = COMP_AASOPTIMIZE; 719 qfiles = GetArgumentFiles(argc, argv, &i, "aas"); 720 } //end else if 721 #endif //ME 722 else 723 { 724 Log_Print("unknown parameter %s\n", argv[i]); 725 break; 726 } //end else 727 } //end for 728 729 //if there are parameters and there's no mismatch in one of the parameters 730 if (argc > 1 && i == argc) 731 { 732 switch(comp) 733 { 734 case COMP_BSP2MAP: 735 { 736 if (!qfiles) Log_Print("no files found\n"); 737 for (qf = qfiles; qf; qf = qf->next) 738 { 739 //copy the output path 740 strcpy(filename, outputpath); 741 //append the bsp file base 742 AppendPathSeperator(filename, MAX_PATH); 743 ExtractFileBase(qf->origname, &filename[strlen(filename)]); 744 //append .map 745 strcat(filename, ".map"); 746 // 747 Log_Print("bsp2map: %s to %s\n", qf->origname, filename); 748 if (qf->type != QFILETYPE_BSP) Warning("%s is probably not a BSP file\n", qf->origname); 749 // 750 LoadMapFromBSP(qf); 751 //write the map file 752 WriteMapFile(filename); 753 } //end for 754 break; 755 } //end case 756 case COMP_BSP2AAS: 757 { 758 if (!qfiles) Log_Print("no files found\n"); 759 for (qf = qfiles; qf; qf = qf->next) 760 { 761 AASOuputFile(qf, outputpath, filename); 762 // 763 Log_Print("bsp2aas: %s to %s\n", qf->origname, filename); 764 if (qf->type != QFILETYPE_BSP) Warning("%s is probably not a BSP file\n", qf->origname); 765 //set before map loading 766 create_aas = 1; 767 LoadMapFromBSP(qf); 768 //create the AAS file 769 AAS_Create(filename); 770 //if it's a Quake3 map calculate the reachabilities and clusters 771 if (loadedmaptype == MAPTYPE_QUAKE3) AAS_CalcReachAndClusters(qf); 772 // 773 if (optimize) AAS_Optimize(); 774 // 775 //write out the stored AAS file 776 if (!AAS_WriteAASFile(filename)) 777 { 778 Error("error writing %s\n", filename); 779 } //end if 780 //deallocate memory 781 AAS_FreeMaxAAS(); 782 } //end for 783 break; 784 } //end case 785 case COMP_REACH: 786 { 787 if (!qfiles) Log_Print("no files found\n"); 788 for (qf = qfiles; qf; qf = qf->next) 789 { 790 AASOuputFile(qf, outputpath, filename); 791 // 792 Log_Print("reach: %s to %s\n", qf->origname, filename); 793 if (qf->type != QFILETYPE_BSP) Warning("%s is probably not a BSP file\n", qf->origname); 794 //if the AAS file exists in the output directory 795 if (!access(filename, 0x04)) 796 { 797 if (!AAS_LoadAASFile(filename, 0, 0)) 798 { 799 Error("error loading aas file %s\n", filename); 800 } //end if 801 //assume it's a Quake3 BSP file 802 loadedmaptype = MAPTYPE_QUAKE3; 803 } //end if 804 else 805 { 806 Warning("AAS file %s not found in output folder\n", filename); 807 Log_Print("creating %s...\n", filename); 808 //set before map loading 809 create_aas = 1; 810 LoadMapFromBSP(qf); 811 //create the AAS file 812 AAS_Create(filename); 813 } //end else 814 //if it's a Quake3 map calculate the reachabilities and clusters 815 if (loadedmaptype == MAPTYPE_QUAKE3) 816 { 817 AAS_CalcReachAndClusters(qf); 818 } //end if 819 // 820 if (optimize) AAS_Optimize(); 821 //write out the stored AAS file 822 if (!AAS_WriteAASFile(filename)) 823 { 824 Error("error writing %s\n", filename); 825 } //end if 826 //deallocate memory 827 AAS_FreeMaxAAS(); 828 } //end for 829 break; 830 } //end case 831 case COMP_CLUSTER: 832 { 833 if (!qfiles) Log_Print("no files found\n"); 834 for (qf = qfiles; qf; qf = qf->next) 835 { 836 AASOuputFile(qf, outputpath, filename); 837 // 838 Log_Print("cluster: %s to %s\n", qf->origname, filename); 839 if (qf->type != QFILETYPE_BSP) Warning("%s is probably not a BSP file\n", qf->origname); 840 //if the AAS file exists in the output directory 841 if (!access(filename, 0x04)) 842 { 843 if (!AAS_LoadAASFile(filename, 0, 0)) 844 { 845 Error("error loading aas file %s\n", filename); 846 } //end if 847 //assume it's a Quake3 BSP file 848 loadedmaptype = MAPTYPE_QUAKE3; 849 //if it's a Quake3 map calculate the clusters 850 if (loadedmaptype == MAPTYPE_QUAKE3) 851 { 852 aasworld.numclusters = 0; 853 AAS_InitBotImport(); 854 AAS_InitClustering(); 855 } //end if 856 } //end if 857 else 858 { 859 Warning("AAS file %s not found in output folder\n", filename); 860 Log_Print("creating %s...\n", filename); 861 //set before map loading 862 create_aas = 1; 863 LoadMapFromBSP(qf); 864 //create the AAS file 865 AAS_Create(filename); 866 //if it's a Quake3 map calculate the reachabilities and clusters 867 if (loadedmaptype == MAPTYPE_QUAKE3) AAS_CalcReachAndClusters(qf); 868 } //end else 869 // 870 if (optimize) AAS_Optimize(); 871 //write out the stored AAS file 872 if (!AAS_WriteAASFile(filename)) 873 { 874 Error("error writing %s\n", filename); 875 } //end if 876 //deallocate memory 877 AAS_FreeMaxAAS(); 878 } //end for 879 break; 880 } //end case 881 case COMP_AASOPTIMIZE: 882 { 883 if (!qfiles) Log_Print("no files found\n"); 884 for (qf = qfiles; qf; qf = qf->next) 885 { 886 AASOuputFile(qf, outputpath, filename); 887 // 888 Log_Print("optimizing: %s to %s\n", qf->origname, filename); 889 if (qf->type != QFILETYPE_AAS) Warning("%s is probably not a AAS file\n", qf->origname); 890 // 891 AAS_InitBotImport(); 892 // 893 if (!AAS_LoadAASFile(qf->filename, qf->offset, qf->length)) 894 { 895 Error("error loading aas file %s\n", qf->filename); 896 } //end if 897 AAS_Optimize(); 898 //write out the stored AAS file 899 if (!AAS_WriteAASFile(filename)) 900 { 901 Error("error writing %s\n", filename); 902 } //end if 903 //deallocate memory 904 AAS_FreeMaxAAS(); 905 } //end for 906 break; 907 } //end case 908 case COMP_AASINFO: 909 { 910 if (!qfiles) Log_Print("no files found\n"); 911 for (qf = qfiles; qf; qf = qf->next) 912 { 913 AASOuputFile(qf, outputpath, filename); 914 // 915 Log_Print("aas info for: %s\n", filename); 916 if (qf->type != QFILETYPE_AAS) Warning("%s is probably not a AAS file\n", qf->origname); 917 // 918 AAS_InitBotImport(); 919 // 920 if (!AAS_LoadAASFile(qf->filename, qf->offset, qf->length)) 921 { 922 Error("error loading aas file %s\n", qf->filename); 923 } //end if 924 AAS_ShowTotals(); 925 } //end for 926 } //end case 927 default: 928 { 929 Log_Print("don't know what to do\n"); 930 break; 931 } //end default 932 } //end switch 933 } //end if 934 else 935 { 936 Log_Print("Usage: bspc [-<switch> [-<switch> ...]]\n" 937 #if defined(WIN32) || defined(_WIN32) 938 "Example 1: bspc -bsp2aas d:\\quake3\\baseq3\\maps\\mymap?.bsp\n" 939 "Example 2: bspc -bsp2aas d:\\quake3\\baseq3\\pak0.pk3\\maps/q3dm*.bsp\n" 940 #else 941 "Example 1: bspc -bsp2aas /quake3/baseq3/maps/mymap?.bsp\n" 942 "Example 2: bspc -bsp2aas /quake3/baseq3/pak0.pk3/maps/q3dm*.bsp\n" 943 #endif 944 "\n" 945 "Switches:\n" 946 //" bsp2map <[pakfilter/]filter.bsp> = convert BSP to MAP\n" 947 //" aasall <quake3folder> = create AAS files for all BSPs\n" 948 " bsp2aas <[pakfilter/]filter.bsp> = convert BSP to AAS\n" 949 " reach <filter.bsp> = compute reachability & clusters\n" 950 " cluster <filter.aas> = compute clusters\n" 951 " aasopt <filter.aas> = optimize aas file\n" 952 " aasinfo <filter.aas> = show AAS file info\n" 953 " output <output path> = set output path\n" 954 " threads <X> = set number of threads to X\n" 955 " cfg <filename> = use this cfg file\n" 956 " optimize = enable optimization\n" 957 " noverbose = disable verbose output\n" 958 " breadthfirst = breadth first bsp building\n" 959 " nobrushmerge = don't merge brushes\n" 960 " noliquids = don't write liquids to map\n" 961 " freetree = free the bsp tree\n" 962 " nocsg = disables brush chopping\n" 963 " forcesidesvisible = force all sides to be visible\n" 964 " grapplereach = calculate grapple reachabilities\n" 965 966 /* " glview = output a GL view\n" 967 " draw = enables drawing\n" 968 " noweld = disables weld\n" 969 " noshare = disables sharing\n" 970 " notjunc = disables juncs\n" 971 " nowater = disables water brushes\n" 972 " noprune = disables node prunes\n" 973 " nomerge = disables face merging\n" 974 " nosubdiv = disables subdeviding\n" 975 " nodetail = disables detail brushes\n" 976 " fulldetail = enables full detail\n" 977 " onlyents = only compile entities with bsp\n" 978 " micro <volume>\n" 979 " = sets the micro volume to the given float\n" 980 " leaktest = perform a leak test\n" 981 " verboseentities\n" 982 " = enable entity verbose mode\n" 983 " chop <subdivide_size>\n" 984 " = sets the subdivide size to the given float\n"*/ 985 "\n"); 986 } //end else 987 Log_Print("BSPC run time is %5.0f seconds\n", I_FloatTime() - start_time); 988 Log_Close(); //close the log file 989 return 0; 990 } //end of the function main 991