00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #if defined(WIN32) || defined(_WIN32)
00024 #include <direct.h>
00025 #include <windows.h>
00026 #include <sys/types.h>
00027 #include <sys/stat.h>
00028 #else
00029 #include <unistd.h>
00030 #include <glob.h>
00031 #include <sys/stat.h>
00032 #include <unistd.h>
00033 #endif
00034 #include "qbsp.h"
00035 #include "l_mem.h"
00036 #include "../botlib/aasfile.h"
00037 #include "../botlib/be_aas_cluster.h"
00038 #include "../botlib/be_aas_optimize.h"
00039 #include "aas_create.h"
00040 #include "aas_store.h"
00041 #include "aas_file.h"
00042 #include "aas_cfg.h"
00043 #include "be_aas_bspc.h"
00044
00045 extern int use_nodequeue;
00046 extern int calcgrapplereach;
00047
00048 float subdivide_size = 240;
00049 char source[1024];
00050 char name[1024];
00051 vec_t microvolume = 1.0;
00052 char outbase[32];
00053 int entity_num;
00054 aas_settings_t aassettings;
00055
00056 qboolean noprune;
00057 qboolean glview;
00058 qboolean nodetail;
00059 qboolean fulldetail;
00060 qboolean onlyents;
00061 qboolean nomerge;
00062 qboolean nowater;
00063 qboolean nocsg;
00064 qboolean noweld;
00065 qboolean noshare;
00066 qboolean nosubdiv;
00067 qboolean notjunc;
00068 qboolean optimize;
00069 qboolean leaktest;
00070 qboolean verboseentities;
00071 qboolean freetree;
00072 qboolean create_aas;
00073 qboolean nobrushmerge;
00074 qboolean lessbrushes;
00075 qboolean cancelconversion;
00076 qboolean noliquids;
00077 qboolean forcesidesvisible;
00078 qboolean capsule_collision = 0;
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321 void AASOuputFile(quakefile_t *qf, char *outputpath, char *filename)
00322 {
00323 char ext[MAX_PATH];
00324
00325
00326 if (strlen(outputpath))
00327 {
00328 strcpy(filename, outputpath);
00329
00330 AppendPathSeperator(filename, MAX_PATH);
00331 ExtractFileBase(qf->origname, &filename[strlen(filename)]);
00332
00333 strcat(filename, ".aas");
00334 return;
00335 }
00336
00337 ExtractFileExtension(qf->filename, ext);
00338 if (!stricmp(ext, "pk3") || !stricmp(ext, "pak") || !stricmp(ext, "sin"))
00339 {
00340 strcpy(filename, qf->filename);
00341 while(strlen(filename) &&
00342 filename[strlen(filename)-1] != '\\' &&
00343 filename[strlen(filename)-1] != '/')
00344 {
00345 filename[strlen(filename)-1] = '\0';
00346 }
00347 strcat(filename, "maps");
00348 if (access(filename, 0x04)) CreatePath(filename);
00349
00350 AppendPathSeperator(filename, MAX_PATH);
00351 ExtractFileBase(qf->origname, &filename[strlen(filename)]);
00352
00353 strcat(filename, ".aas");
00354 }
00355 else
00356 {
00357 strcpy(filename, qf->filename);
00358 while(strlen(filename) &&
00359 filename[strlen(filename)-1] != '.')
00360 {
00361 filename[strlen(filename)-1] = '\0';
00362 }
00363 strcat(filename, "aas");
00364 }
00365 }
00366
00367
00368
00369
00370
00371
00372 void CreateAASFilesForAllBSPFiles(char *quakepath)
00373 {
00374 #if defined(WIN32)|defined(_WIN32)
00375 WIN32_FIND_DATA filedata;
00376 HWND handle;
00377 struct _stat statbuf;
00378 #else
00379 glob_t globbuf;
00380 struct stat statbuf;
00381 int j;
00382 #endif
00383 int done;
00384 char filter[_MAX_PATH], bspfilter[_MAX_PATH], aasfilter[_MAX_PATH];
00385 char aasfile[_MAX_PATH], buf[_MAX_PATH], foldername[_MAX_PATH];
00386 quakefile_t *qf, *qf2, *files, *bspfiles, *aasfiles;
00387
00388 strcpy(filter, quakepath);
00389 AppendPathSeperator(filter, sizeof(filter));
00390 strcat(filter, "*");
00391
00392 #if defined(WIN32)|defined(_WIN32)
00393 handle = FindFirstFile(filter, &filedata);
00394 done = (handle == INVALID_HANDLE_VALUE);
00395 while(!done)
00396 {
00397 _splitpath(filter, foldername, NULL, NULL, NULL);
00398 _splitpath(filter, NULL, &foldername[strlen(foldername)], NULL, NULL);
00399 AppendPathSeperator(foldername, _MAX_PATH);
00400 strcat(foldername, filedata.cFileName);
00401 _stat(foldername, &statbuf);
00402 #else
00403 glob(filter, 0, NULL, &globbuf);
00404 for (j = 0; j < globbuf.gl_pathc; j++)
00405 {
00406 strcpy(foldername, globbuf.gl_pathv[j]);
00407 stat(foldername, &statbuf);
00408 #endif
00409
00410 if (statbuf.st_mode & S_IFDIR)
00411 {
00412
00413 AppendPathSeperator(foldername, sizeof(foldername));
00414
00415 strcpy(bspfilter, foldername);
00416 strcat(bspfilter, "maps/*.bsp");
00417 files = FindQuakeFiles(bspfilter);
00418 strcpy(bspfilter, foldername);
00419 strcat(bspfilter, "*.pk3/maps/*.bsp");
00420 bspfiles = FindQuakeFiles(bspfilter);
00421 for (qf = bspfiles; qf; qf = qf->next) if (!qf->next) break;
00422 if (qf) qf->next = files;
00423 else bspfiles = files;
00424
00425 strcpy(aasfilter, foldername);
00426 strcat(aasfilter, "maps/*.aas");
00427 files = FindQuakeFiles(aasfilter);
00428 strcpy(aasfilter, foldername);
00429 strcat(aasfilter, "*.pk3/maps/*.aas");
00430 aasfiles = FindQuakeFiles(aasfilter);
00431 for (qf = aasfiles; qf; qf = qf->next) if (!qf->next) break;
00432 if (qf) qf->next = files;
00433 else aasfiles = files;
00434
00435 for (qf = bspfiles; qf; qf = qf->next)
00436 {
00437 sprintf(aasfile, "%s/%s", qf->pakfile, qf->origname);
00438 Log_Print("found %s\n", aasfile);
00439 strcpy(&aasfile[strlen(aasfile)-strlen(".bsp")], ".aas");
00440 for (qf2 = aasfiles; qf2; qf2 = qf2->next)
00441 {
00442 sprintf(buf, "%s/%s", qf2->pakfile, qf2->origname);
00443 if (!stricmp(aasfile, buf))
00444 {
00445 Log_Print("found %s\n", buf);
00446 break;
00447 }
00448 }
00449 }
00450 }
00451 #if defined(WIN32)|defined(_WIN32)
00452
00453 done = !FindNextFile(handle, &filedata);
00454 }
00455 #else
00456 }
00457 globfree(&globbuf);
00458 #endif
00459 }
00460
00461
00462
00463
00464
00465
00466 quakefile_t *GetArgumentFiles(int argc, char *argv[], int *i, char *ext)
00467 {
00468 quakefile_t *qfiles, *lastqf, *qf;
00469 int j;
00470 char buf[1024];
00471
00472 qfiles = NULL;
00473 lastqf = NULL;
00474 for (; (*i)+1 < argc && argv[(*i)+1][0] != '-'; (*i)++)
00475 {
00476 strcpy(buf, argv[(*i)+1]);
00477 for (j = strlen(buf)-1; j >= strlen(buf)-4; j--)
00478 if (buf[j] == '.') break;
00479 if (j >= strlen(buf)-4)
00480 strcpy(&buf[j+1], ext);
00481 qf = FindQuakeFiles(buf);
00482 if (!qf) continue;
00483 if (lastqf) lastqf->next = qf;
00484 else qfiles = qf;
00485 lastqf = qf;
00486 while(lastqf->next) lastqf = lastqf->next;
00487 }
00488 return qfiles;
00489 }
00490
00491
00492
00493
00494
00495
00496
00497 #define COMP_BSP2MAP 1
00498 #define COMP_BSP2AAS 2
00499 #define COMP_REACH 3
00500 #define COMP_CLUSTER 4
00501 #define COMP_AASOPTIMIZE 5
00502 #define COMP_AASINFO 6
00503
00504 int main (int argc, char **argv)
00505 {
00506 int i, comp = 0;
00507 char outputpath[MAX_PATH] = "";
00508 char filename[MAX_PATH] = "unknown";
00509 quakefile_t *qfiles, *qf;
00510 double start_time;
00511
00512 myargc = argc;
00513 myargv = argv;
00514
00515 start_time = I_FloatTime();
00516
00517 Log_Open("bspc.log");
00518 Log_Print("BSPC version "BSPC_VERSION", %s %s\n", __DATE__, __TIME__);
00519
00520 DefaultCfg();
00521 for (i = 1; i < argc; i++)
00522 {
00523 if (!stricmp(argv[i],"-threads"))
00524 {
00525 if (i + 1 >= argc) {i = 0; break;}
00526 numthreads = atoi(argv[++i]);
00527 Log_Print("threads = %d\n", numthreads);
00528 }
00529 else if (!stricmp(argv[i], "-noverbose"))
00530 {
00531 Log_Print("verbose = false\n");
00532 verbose = false;
00533 }
00534 else if (!stricmp(argv[i], "-nocsg"))
00535 {
00536 Log_Print("nocsg = true\n");
00537 nocsg = true;
00538 }
00539 else if (!stricmp(argv[i], "-optimize"))
00540 {
00541 Log_Print("optimize = true\n");
00542 optimize = true;
00543 }
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632 #ifdef ME
00633 else if (!stricmp(argv[i], "-freetree"))
00634 {
00635 freetree = true;
00636 Log_Print("freetree = true\n");
00637 }
00638 else if (!stricmp(argv[i], "-grapplereach"))
00639 {
00640 calcgrapplereach = true;
00641 Log_Print("grapplereach = true\n");
00642 }
00643 else if (!stricmp(argv[i], "-nobrushmerge"))
00644 {
00645 nobrushmerge = true;
00646 Log_Print("nobrushmerge = true\n");
00647 }
00648 else if (!stricmp(argv[i], "-noliquids"))
00649 {
00650 noliquids = true;
00651 Log_Print("noliquids = true\n");
00652 }
00653 else if (!stricmp(argv[i], "-forcesidesvisible"))
00654 {
00655 forcesidesvisible = true;
00656 Log_Print("forcesidesvisible = true\n");
00657 }
00658 else if (!stricmp(argv[i], "-output"))
00659 {
00660 if (i + 1 >= argc) {i = 0; break;}
00661 if (access(argv[i+1], 0x04)) Warning("the folder %s does not exist", argv[i+1]);
00662 strcpy(outputpath, argv[++i]);
00663 }
00664 else if (!stricmp(argv[i], "-breadthfirst"))
00665 {
00666 use_nodequeue = true;
00667 Log_Print("breadthfirst = true\n");
00668 }
00669 else if (!stricmp(argv[i], "-capsule"))
00670 {
00671 capsule_collision = true;
00672 Log_Print("capsule_collision = true\n");
00673 }
00674 else if (!stricmp(argv[i], "-cfg"))
00675 {
00676 if (i + 1 >= argc) {i = 0; break;}
00677 if (!LoadCfgFile(argv[++i]))
00678 exit(0);
00679 }
00680 else if (!stricmp(argv[i], "-bsp2map"))
00681 {
00682 if (i + 1 >= argc) {i = 0; break;}
00683 comp = COMP_BSP2MAP;
00684 qfiles = GetArgumentFiles(argc, argv, &i, "bsp");
00685 }
00686 else if (!stricmp(argv[i], "-bsp2aas"))
00687 {
00688 if (i + 1 >= argc) {i = 0; break;}
00689 comp = COMP_BSP2AAS;
00690 qfiles = GetArgumentFiles(argc, argv, &i, "bsp");
00691 }
00692 else if (!stricmp(argv[i], "-aasall"))
00693 {
00694 if (i + 1 >= argc) {i = 0; break;}
00695 CreateAASFilesForAllBSPFiles(argv[++i]);
00696 }
00697 else if (!stricmp(argv[i], "-reach"))
00698 {
00699 if (i + 1 >= argc) {i = 0; break;}
00700 comp = COMP_REACH;
00701 qfiles = GetArgumentFiles(argc, argv, &i, "bsp");
00702 }
00703 else if (!stricmp(argv[i], "-cluster"))
00704 {
00705 if (i + 1 >= argc) {i = 0; break;}
00706 comp = COMP_CLUSTER;
00707 qfiles = GetArgumentFiles(argc, argv, &i, "bsp");
00708 }
00709 else if (!stricmp(argv[i], "-aasinfo"))
00710 {
00711 if (i + 1 >= argc) {i = 0; break;}
00712 comp = COMP_AASINFO;
00713 qfiles = GetArgumentFiles(argc, argv, &i, "aas");
00714 }
00715 else if (!stricmp(argv[i], "-aasopt"))
00716 {
00717 if (i + 1 >= argc) {i = 0; break;}
00718 comp = COMP_AASOPTIMIZE;
00719 qfiles = GetArgumentFiles(argc, argv, &i, "aas");
00720 }
00721 #endif //ME
00722 else
00723 {
00724 Log_Print("unknown parameter %s\n", argv[i]);
00725 break;
00726 }
00727 }
00728
00729
00730 if (argc > 1 && i == argc)
00731 {
00732 switch(comp)
00733 {
00734 case COMP_BSP2MAP:
00735 {
00736 if (!qfiles) Log_Print("no files found\n");
00737 for (qf = qfiles; qf; qf = qf->next)
00738 {
00739
00740 strcpy(filename, outputpath);
00741
00742 AppendPathSeperator(filename, MAX_PATH);
00743 ExtractFileBase(qf->origname, &filename[strlen(filename)]);
00744
00745 strcat(filename, ".map");
00746
00747 Log_Print("bsp2map: %s to %s\n", qf->origname, filename);
00748 if (qf->type != QFILETYPE_BSP) Warning("%s is probably not a BSP file\n", qf->origname);
00749
00750 LoadMapFromBSP(qf);
00751
00752 WriteMapFile(filename);
00753 }
00754 break;
00755 }
00756 case COMP_BSP2AAS:
00757 {
00758 if (!qfiles) Log_Print("no files found\n");
00759 for (qf = qfiles; qf; qf = qf->next)
00760 {
00761 AASOuputFile(qf, outputpath, filename);
00762
00763 Log_Print("bsp2aas: %s to %s\n", qf->origname, filename);
00764 if (qf->type != QFILETYPE_BSP) Warning("%s is probably not a BSP file\n", qf->origname);
00765
00766 create_aas = 1;
00767 LoadMapFromBSP(qf);
00768
00769 AAS_Create(filename);
00770
00771 if (loadedmaptype == MAPTYPE_QUAKE3) AAS_CalcReachAndClusters(qf);
00772
00773 if (optimize) AAS_Optimize();
00774
00775
00776 if (!AAS_WriteAASFile(filename))
00777 {
00778 Error("error writing %s\n", filename);
00779 }
00780
00781 AAS_FreeMaxAAS();
00782 }
00783 break;
00784 }
00785 case COMP_REACH:
00786 {
00787 if (!qfiles) Log_Print("no files found\n");
00788 for (qf = qfiles; qf; qf = qf->next)
00789 {
00790 AASOuputFile(qf, outputpath, filename);
00791
00792 Log_Print("reach: %s to %s\n", qf->origname, filename);
00793 if (qf->type != QFILETYPE_BSP) Warning("%s is probably not a BSP file\n", qf->origname);
00794
00795 if (!access(filename, 0x04))
00796 {
00797 if (!AAS_LoadAASFile(filename, 0, 0))
00798 {
00799 Error("error loading aas file %s\n", filename);
00800 }
00801
00802 loadedmaptype = MAPTYPE_QUAKE3;
00803 }
00804 else
00805 {
00806 Warning("AAS file %s not found in output folder\n", filename);
00807 Log_Print("creating %s...\n", filename);
00808
00809 create_aas = 1;
00810 LoadMapFromBSP(qf);
00811
00812 AAS_Create(filename);
00813 }
00814
00815 if (loadedmaptype == MAPTYPE_QUAKE3)
00816 {
00817 AAS_CalcReachAndClusters(qf);
00818 }
00819
00820 if (optimize) AAS_Optimize();
00821
00822 if (!AAS_WriteAASFile(filename))
00823 {
00824 Error("error writing %s\n", filename);
00825 }
00826
00827 AAS_FreeMaxAAS();
00828 }
00829 break;
00830 }
00831 case COMP_CLUSTER:
00832 {
00833 if (!qfiles) Log_Print("no files found\n");
00834 for (qf = qfiles; qf; qf = qf->next)
00835 {
00836 AASOuputFile(qf, outputpath, filename);
00837
00838 Log_Print("cluster: %s to %s\n", qf->origname, filename);
00839 if (qf->type != QFILETYPE_BSP) Warning("%s is probably not a BSP file\n", qf->origname);
00840
00841 if (!access(filename, 0x04))
00842 {
00843 if (!AAS_LoadAASFile(filename, 0, 0))
00844 {
00845 Error("error loading aas file %s\n", filename);
00846 }
00847
00848 loadedmaptype = MAPTYPE_QUAKE3;
00849
00850 if (loadedmaptype == MAPTYPE_QUAKE3)
00851 {
00852 aasworld.numclusters = 0;
00853 AAS_InitBotImport();
00854 AAS_InitClustering();
00855 }
00856 }
00857 else
00858 {
00859 Warning("AAS file %s not found in output folder\n", filename);
00860 Log_Print("creating %s...\n", filename);
00861
00862 create_aas = 1;
00863 LoadMapFromBSP(qf);
00864
00865 AAS_Create(filename);
00866
00867 if (loadedmaptype == MAPTYPE_QUAKE3) AAS_CalcReachAndClusters(qf);
00868 }
00869
00870 if (optimize) AAS_Optimize();
00871
00872 if (!AAS_WriteAASFile(filename))
00873 {
00874 Error("error writing %s\n", filename);
00875 }
00876
00877 AAS_FreeMaxAAS();
00878 }
00879 break;
00880 }
00881 case COMP_AASOPTIMIZE:
00882 {
00883 if (!qfiles) Log_Print("no files found\n");
00884 for (qf = qfiles; qf; qf = qf->next)
00885 {
00886 AASOuputFile(qf, outputpath, filename);
00887
00888 Log_Print("optimizing: %s to %s\n", qf->origname, filename);
00889 if (qf->type != QFILETYPE_AAS) Warning("%s is probably not a AAS file\n", qf->origname);
00890
00891 AAS_InitBotImport();
00892
00893 if (!AAS_LoadAASFile(qf->filename, qf->offset, qf->length))
00894 {
00895 Error("error loading aas file %s\n", qf->filename);
00896 }
00897 AAS_Optimize();
00898
00899 if (!AAS_WriteAASFile(filename))
00900 {
00901 Error("error writing %s\n", filename);
00902 }
00903
00904 AAS_FreeMaxAAS();
00905 }
00906 break;
00907 }
00908 case COMP_AASINFO:
00909 {
00910 if (!qfiles) Log_Print("no files found\n");
00911 for (qf = qfiles; qf; qf = qf->next)
00912 {
00913 AASOuputFile(qf, outputpath, filename);
00914
00915 Log_Print("aas info for: %s\n", filename);
00916 if (qf->type != QFILETYPE_AAS) Warning("%s is probably not a AAS file\n", qf->origname);
00917
00918 AAS_InitBotImport();
00919
00920 if (!AAS_LoadAASFile(qf->filename, qf->offset, qf->length))
00921 {
00922 Error("error loading aas file %s\n", qf->filename);
00923 }
00924 AAS_ShowTotals();
00925 }
00926 }
00927 default:
00928 {
00929 Log_Print("don't know what to do\n");
00930 break;
00931 }
00932 }
00933 }
00934 else
00935 {
00936 Log_Print("Usage: bspc [-<switch> [-<switch> ...]]\n"
00937 #if defined(WIN32) || defined(_WIN32)
00938 "Example 1: bspc -bsp2aas d:\\quake3\\baseq3\\maps\\mymap?.bsp\n"
00939 "Example 2: bspc -bsp2aas d:\\quake3\\baseq3\\pak0.pk3\\maps/q3dm*.bsp\n"
00940 #else
00941 "Example 1: bspc -bsp2aas /quake3/baseq3/maps/mymap?.bsp\n"
00942 "Example 2: bspc -bsp2aas /quake3/baseq3/pak0.pk3/maps/q3dm*.bsp\n"
00943 #endif
00944 "\n"
00945 "Switches:\n"
00946
00947
00948 " bsp2aas <[pakfilter/]filter.bsp> = convert BSP to AAS\n"
00949 " reach <filter.bsp> = compute reachability & clusters\n"
00950 " cluster <filter.aas> = compute clusters\n"
00951 " aasopt <filter.aas> = optimize aas file\n"
00952 " aasinfo <filter.aas> = show AAS file info\n"
00953 " output <output path> = set output path\n"
00954 " threads <X> = set number of threads to X\n"
00955 " cfg <filename> = use this cfg file\n"
00956 " optimize = enable optimization\n"
00957 " noverbose = disable verbose output\n"
00958 " breadthfirst = breadth first bsp building\n"
00959 " nobrushmerge = don't merge brushes\n"
00960 " noliquids = don't write liquids to map\n"
00961 " freetree = free the bsp tree\n"
00962 " nocsg = disables brush chopping\n"
00963 " forcesidesvisible = force all sides to be visible\n"
00964 " grapplereach = calculate grapple reachabilities\n"
00965
00966
00967
00968
00969
00970
00971
00972
00973
00974
00975
00976
00977
00978
00979
00980
00981
00982
00983
00984
00985 "\n");
00986 }
00987 Log_Print("BSPC run time is %5.0f seconds\n", I_FloatTime() - start_time);
00988 Log_Close();
00989 return 0;
00990 }
00991