mirror of
https://github.com/DrBeef/ioq3quest.git
synced 2024-11-30 07:41:16 +00:00
991 lines
28 KiB
C
991 lines
28 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
|
|
This file is part of Quake III Arena source code.
|
|
|
|
Quake III Arena 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 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
Quake III Arena 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 Foobar; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
|
#include <direct.h>
|
|
#include <windows.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#include <glob.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
#include "qbsp.h"
|
|
#include "l_mem.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"
|
|
|
|
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 capsule_collision = 0;
|
|
|
|
/*
|
|
//===========================================================================
|
|
//
|
|
// 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)]);
|
|
//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)]);
|
|
//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
|
|
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;
|
|
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
|
|
|
|
int main (int argc, char **argv)
|
|
{
|
|
int i, comp = 0;
|
|
char outputpath[MAX_PATH] = "";
|
|
char filename[MAX_PATH] = "unknown";
|
|
quakefile_t *qfiles, *qf;
|
|
double start_time;
|
|
|
|
myargc = argc;
|
|
myargv = argv;
|
|
|
|
start_time = I_FloatTime();
|
|
|
|
Log_Open("bspc.log"); //open a log file
|
|
Log_Print("BSPC version "BSPC_VERSION", %s %s\n", __DATE__, __TIME__);
|
|
|
|
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], "-capsule"))
|
|
{
|
|
capsule_collision = true;
|
|
Log_Print("capsule_collision = 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
|
|
#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
|
|
default:
|
|
{
|
|
Log_Print("don't know what to do\n");
|
|
break;
|
|
} //end default
|
|
} //end switch
|
|
} //end if
|
|
else
|
|
{
|
|
Log_Print("Usage: bspc [-<switch> [-<switch> ...]]\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"
|
|
//" aasall <quake3folder> = create AAS files for all BSPs\n"
|
|
" bsp2aas <[pakfilter/]filter.bsp> = convert BSP to AAS\n"
|
|
" reach <filter.bsp> = compute reachability & clusters\n"
|
|
" cluster <filter.aas> = compute clusters\n"
|
|
" aasopt <filter.aas> = optimize aas file\n"
|
|
" aasinfo <filter.aas> = show AAS file info\n"
|
|
" output <output path> = set output path\n"
|
|
" threads <X> = set number of threads to X\n"
|
|
" cfg <filename> = 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"
|
|
|
|
/* " 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 <volume>\n"
|
|
" = sets the micro volume to the given float\n"
|
|
" leaktest = perform a leak test\n"
|
|
" verboseentities\n"
|
|
" = enable entity verbose mode\n"
|
|
" chop <subdivide_size>\n"
|
|
" = sets the subdivide size to the given float\n"*/
|
|
"\n");
|
|
} //end else
|
|
Log_Print("BSPC run time is %5.0f seconds\n", I_FloatTime() - start_time);
|
|
Log_Close(); //close the log file
|
|
return 0;
|
|
} //end of the function main
|
|
|