605 lines
13 KiB
C
Executable file
605 lines
13 KiB
C
Executable file
/*
|
|
===========================================================================
|
|
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
|
|
===========================================================================
|
|
*/
|
|
#include "qbsp.h"
|
|
|
|
#ifdef _WIN32
|
|
#ifdef _TTIMOBUILD
|
|
#include "pakstuff.h"
|
|
#else
|
|
#include "../libs/pakstuff.h"
|
|
#endif
|
|
extern HWND hwndOut;
|
|
#endif
|
|
|
|
char source[1024];
|
|
char tempsource[1024];
|
|
char name[1024];
|
|
|
|
vec_t microvolume = 1.0;
|
|
qboolean glview;
|
|
qboolean nodetail;
|
|
qboolean fulldetail;
|
|
qboolean onlyents;
|
|
qboolean onlytextures;
|
|
qboolean nowater;
|
|
qboolean nofill;
|
|
qboolean noopt;
|
|
qboolean leaktest;
|
|
qboolean verboseentities;
|
|
qboolean noCurveBrushes;
|
|
qboolean fakemap;
|
|
qboolean notjunc;
|
|
qboolean nomerge;
|
|
qboolean nofog;
|
|
qboolean nosubdivide;
|
|
qboolean testExpand;
|
|
qboolean showseams;
|
|
|
|
char outbase[32];
|
|
|
|
int entity_num;
|
|
|
|
/*
|
|
============
|
|
ProcessWorldModel
|
|
|
|
============
|
|
*/
|
|
void ProcessWorldModel( void ) {
|
|
entity_t *e;
|
|
tree_t *tree;
|
|
bspface_t *faces;
|
|
qboolean leaked;
|
|
|
|
BeginModel();
|
|
|
|
e = &entities[0];
|
|
e->firstDrawSurf = 0;//numMapDrawSurfs;
|
|
|
|
// check for patches with adjacent edges that need to LOD together
|
|
PatchMapDrawSurfs( e );
|
|
|
|
// build an initial bsp tree using all of the sides
|
|
// of all of the structural brushes
|
|
faces = MakeStructuralBspFaceList ( entities[0].brushes );
|
|
tree = FaceBSP( faces );
|
|
MakeTreePortals (tree);
|
|
FilterStructuralBrushesIntoTree( e, tree );
|
|
|
|
// see if the bsp is completely enclosed
|
|
if ( FloodEntities (tree) ) {
|
|
// rebuild a better bsp tree using only the
|
|
// sides that are visible from the inside
|
|
FillOutside (tree->headnode);
|
|
|
|
// chop the sides to the convex hull of
|
|
// their visible fragments, giving us the smallest
|
|
// polygons
|
|
ClipSidesIntoTree( e, tree );
|
|
|
|
faces = MakeVisibleBspFaceList( entities[0].brushes );
|
|
FreeTree (tree);
|
|
tree = FaceBSP( faces );
|
|
MakeTreePortals( tree );
|
|
FilterStructuralBrushesIntoTree( e, tree );
|
|
leaked = qfalse;
|
|
} else {
|
|
_printf ("**********************\n");
|
|
_printf ("******* leaked *******\n");
|
|
_printf ("**********************\n");
|
|
LeakFile (tree);
|
|
if ( leaktest ) {
|
|
_printf ("--- MAP LEAKED, ABORTING LEAKTEST ---\n");
|
|
exit (0);
|
|
}
|
|
leaked = qtrue;
|
|
|
|
// chop the sides to the convex hull of
|
|
// their visible fragments, giving us the smallest
|
|
// polygons
|
|
ClipSidesIntoTree( e, tree );
|
|
}
|
|
|
|
// save out information for visibility processing
|
|
NumberClusters( tree );
|
|
if ( !leaked ) {
|
|
WritePortalFile( tree );
|
|
}
|
|
if ( glview ) {
|
|
// dump the portals for debugging
|
|
WriteGLView( tree, source );
|
|
}
|
|
FloodAreas (tree);
|
|
|
|
// add references to the detail brushes
|
|
FilterDetailBrushesIntoTree( e, tree );
|
|
|
|
// create drawsurfs for triangle models
|
|
AddTriangleModels( tree );
|
|
|
|
// drawsurfs that cross fog boundaries will need to
|
|
// be split along the bound
|
|
if ( !nofog ) {
|
|
FogDrawSurfs(); // may fragment drawsurfs
|
|
}
|
|
|
|
// subdivide each drawsurf as required by shader tesselation
|
|
if ( !nosubdivide ) {
|
|
SubdivideDrawSurfs( e, tree );
|
|
}
|
|
|
|
// merge together all common shaders on the same plane and remove
|
|
// all colinear points, so extra tjunctions won't be generated
|
|
if ( !nomerge ) {
|
|
MergeSides( e, tree ); // !@# testing
|
|
}
|
|
|
|
// add in any vertexes required to fix tjunctions
|
|
if ( !notjunc ) {
|
|
FixTJunctions( e );
|
|
}
|
|
|
|
// allocate lightmaps for faces and patches
|
|
AllocateLightmaps( e );
|
|
|
|
// add references to the final drawsurfs in the apropriate clusters
|
|
FilterDrawsurfsIntoTree( e, tree );
|
|
|
|
EndModel( tree->headnode );
|
|
|
|
FreeTree (tree);
|
|
}
|
|
|
|
/*
|
|
============
|
|
ProcessSubModel
|
|
|
|
============
|
|
*/
|
|
void ProcessSubModel( void ) {
|
|
entity_t *e;
|
|
tree_t *tree;
|
|
bspbrush_t *b, *bc;
|
|
node_t *node;
|
|
|
|
BeginModel ();
|
|
|
|
e = &entities[entity_num];
|
|
e->firstDrawSurf = numMapDrawSurfs;
|
|
|
|
PatchMapDrawSurfs( e );
|
|
|
|
// just put all the brushes in an empty leaf
|
|
// FIXME: patches?
|
|
node = AllocNode();
|
|
node->planenum = PLANENUM_LEAF;
|
|
for ( b = e->brushes ; b ; b = b->next ) {
|
|
bc = CopyBrush( b );
|
|
bc->next = node->brushlist;
|
|
node->brushlist = bc;
|
|
}
|
|
|
|
tree = AllocTree();
|
|
tree->headnode = node;
|
|
|
|
ClipSidesIntoTree( e, tree );
|
|
|
|
// subdivide each drawsurf as required by shader tesselation or fog
|
|
if ( !nosubdivide ) {
|
|
SubdivideDrawSurfs( e, tree );
|
|
}
|
|
|
|
// merge together all common shaders on the same plane and remove
|
|
// all colinear points, so extra tjunctions won't be generated
|
|
if ( !nomerge ) {
|
|
MergeSides( e, tree ); // !@# testing
|
|
}
|
|
|
|
// add in any vertexes required to fix tjunctions
|
|
if ( !notjunc ) {
|
|
FixTJunctions( e );
|
|
}
|
|
|
|
// allocate lightmaps for faces and patches
|
|
AllocateLightmaps( e );
|
|
|
|
// add references to the final drawsurfs in the apropriate clusters
|
|
FilterDrawsurfsIntoTree( e, tree );
|
|
|
|
EndModel ( node );
|
|
|
|
FreeTree( tree );
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
ProcessModels
|
|
============
|
|
*/
|
|
void ProcessModels (void)
|
|
{
|
|
qboolean oldVerbose;
|
|
entity_t *entity;
|
|
|
|
oldVerbose = verbose;
|
|
|
|
BeginBSPFile ();
|
|
|
|
for ( entity_num=0 ; entity_num< num_entities ; entity_num++ ) {
|
|
entity = &entities[entity_num];
|
|
|
|
if ( !entity->brushes && !entity->patches ) {
|
|
continue;
|
|
}
|
|
|
|
qprintf ("############### model %i ###############\n", nummodels);
|
|
if (entity_num == 0)
|
|
ProcessWorldModel ();
|
|
else
|
|
ProcessSubModel ();
|
|
|
|
if (!verboseentities)
|
|
verbose = qfalse; // don't bother printing submodels
|
|
}
|
|
|
|
verbose = oldVerbose;
|
|
}
|
|
|
|
/*
|
|
============
|
|
Bspinfo
|
|
============
|
|
*/
|
|
void Bspinfo( int count, char **fileNames ) {
|
|
int i;
|
|
char source[1024];
|
|
int size;
|
|
FILE *f;
|
|
|
|
if ( count < 1 ) {
|
|
_printf( "No files to dump info for.\n");
|
|
return;
|
|
}
|
|
|
|
for ( i = 0 ; i < count ; i++ ) {
|
|
_printf ("---------------------\n");
|
|
strcpy (source, fileNames[ i ] );
|
|
DefaultExtension (source, ".bsp");
|
|
f = fopen (source, "rb");
|
|
if (f)
|
|
{
|
|
size = Q_filelength (f);
|
|
fclose (f);
|
|
}
|
|
else
|
|
size = 0;
|
|
_printf ("%s: %i\n", source, size);
|
|
|
|
LoadBSPFile (source);
|
|
PrintBSPFileSizes ();
|
|
_printf ("---------------------\n");
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
OnlyEnts
|
|
============
|
|
*/
|
|
void OnlyEnts( void ) {
|
|
char out[1024];
|
|
|
|
sprintf (out, "%s.bsp", source);
|
|
LoadBSPFile (out);
|
|
num_entities = 0;
|
|
|
|
LoadMapFile (name);
|
|
SetModelNumbers ();
|
|
SetLightStyles ();
|
|
|
|
UnparseEntities ();
|
|
|
|
WriteBSPFile (out);
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
OnlyTextures
|
|
============
|
|
*/
|
|
void OnlyTextures( void ) { // FIXME!!!
|
|
char out[1024];
|
|
int i;
|
|
|
|
Error( "-onlytextures isn't working now..." );
|
|
|
|
sprintf (out, "%s.bsp", source);
|
|
|
|
LoadMapFile (name);
|
|
|
|
LoadBSPFile (out);
|
|
|
|
// replace all the drawsurface shader names
|
|
for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
|
|
}
|
|
|
|
WriteBSPFile (out);
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
main
|
|
============
|
|
*/
|
|
int LightMain( int argc, char **argv );
|
|
int VLightMain (int argc, char **argv);
|
|
int VSoundMain (int argc, char **argv);
|
|
int VisMain( int argc, char **argv );
|
|
|
|
int main (int argc, char **argv) {
|
|
int i;
|
|
double start, end;
|
|
char path[1024];
|
|
|
|
_printf ("Q3Map v1.0s (c) 1999 Id Software Inc.\n");
|
|
|
|
if ( argc < 2 ) {
|
|
Error ("usage: q3map [options] mapfile");
|
|
}
|
|
|
|
// check for general program options
|
|
if (!strcmp(argv[1], "-info")) {
|
|
Bspinfo( argc - 2, argv + 2 );
|
|
return 0;
|
|
}
|
|
if (!strcmp(argv[1], "-light")) {
|
|
LightMain( argc - 1, argv + 1 );
|
|
return 0;
|
|
}
|
|
if (!strcmp(argv[1], "-vlight")) {
|
|
VLightMain( argc - 1, argv + 1 );
|
|
return 0;
|
|
}
|
|
if (!strcmp(argv[1], "-vsound")) {
|
|
VSoundMain( argc - 1, argv + 1 );
|
|
return 0;
|
|
}
|
|
if (!strcmp(argv[1], "-vis")) {
|
|
VisMain( argc - 1, argv + 1 );
|
|
return 0;
|
|
}
|
|
|
|
// do a bsp if nothing else was specified
|
|
|
|
_printf ("---- q3map ----\n");
|
|
|
|
tempsource[0] = '\0';
|
|
|
|
for (i=1 ; i<argc ; i++)
|
|
{
|
|
if (!strcmp(argv[i],"-tempname"))
|
|
{
|
|
strcpy(tempsource, argv[++i]);
|
|
}
|
|
else if (!strcmp(argv[i],"-threads"))
|
|
{
|
|
numthreads = atoi (argv[i+1]);
|
|
i++;
|
|
}
|
|
else if (!strcmp(argv[i],"-glview"))
|
|
{
|
|
glview = qtrue;
|
|
}
|
|
else if (!strcmp(argv[i], "-v"))
|
|
{
|
|
_printf ("verbose = true\n");
|
|
verbose = qtrue;
|
|
}
|
|
else if (!strcmp(argv[i], "-draw"))
|
|
{
|
|
_printf ("drawflag = true\n");
|
|
drawflag = qtrue;
|
|
}
|
|
else if (!strcmp(argv[i], "-nowater"))
|
|
{
|
|
_printf ("nowater = true\n");
|
|
nowater = qtrue;
|
|
}
|
|
else if (!strcmp(argv[i], "-noopt"))
|
|
{
|
|
_printf ("noopt = true\n");
|
|
noopt = qtrue;
|
|
}
|
|
else if (!strcmp(argv[i], "-nofill"))
|
|
{
|
|
_printf ("nofill = true\n");
|
|
nofill = qtrue;
|
|
}
|
|
else if (!strcmp(argv[i], "-nodetail"))
|
|
{
|
|
_printf ("nodetail = true\n");
|
|
nodetail = qtrue;
|
|
}
|
|
else if (!strcmp(argv[i], "-fulldetail"))
|
|
{
|
|
_printf ("fulldetail = true\n");
|
|
fulldetail = qtrue;
|
|
}
|
|
else if (!strcmp(argv[i], "-onlyents"))
|
|
{
|
|
_printf ("onlyents = true\n");
|
|
onlyents = qtrue;
|
|
}
|
|
else if (!strcmp(argv[i], "-onlytextures"))
|
|
{
|
|
_printf ("onlytextures = true\n"); // FIXME: make work again!
|
|
onlytextures = qtrue;
|
|
}
|
|
else if (!strcmp(argv[i], "-micro"))
|
|
{
|
|
microvolume = atof(argv[i+1]);
|
|
_printf ("microvolume = %f\n", microvolume);
|
|
i++;
|
|
}
|
|
else if (!strcmp(argv[i], "-nofog"))
|
|
{
|
|
_printf ("nofog = true\n");
|
|
nofog = qtrue;
|
|
}
|
|
else if (!strcmp(argv[i], "-nosubdivide"))
|
|
{
|
|
_printf ("nosubdivide = true\n");
|
|
nosubdivide = qtrue;
|
|
}
|
|
else if (!strcmp(argv[i], "-leaktest"))
|
|
{
|
|
_printf ("leaktest = true\n");
|
|
leaktest = qtrue;
|
|
}
|
|
else if (!strcmp(argv[i], "-verboseentities"))
|
|
{
|
|
_printf ("verboseentities = true\n");
|
|
verboseentities = qtrue;
|
|
}
|
|
else if (!strcmp(argv[i], "-nocurves"))
|
|
{
|
|
noCurveBrushes = qtrue;
|
|
_printf ("no curve brushes\n");
|
|
}
|
|
else if (!strcmp(argv[i], "-notjunc"))
|
|
{
|
|
notjunc = qtrue;
|
|
_printf ("no tjunction fixing\n");
|
|
}
|
|
else if (!strcmp(argv[i], "-expand"))
|
|
{
|
|
testExpand = qtrue;
|
|
_printf ("Writing expanded.map.\n");
|
|
}
|
|
else if (!strcmp(argv[i], "-showseams"))
|
|
{
|
|
showseams = qtrue;
|
|
_printf ("Showing seams on terrain.\n");
|
|
}
|
|
else if (!strcmp (argv[i],"-tmpout"))
|
|
{
|
|
strcpy (outbase, "/tmp");
|
|
}
|
|
else if (!strcmp (argv[i],"-fakemap"))
|
|
{
|
|
fakemap = qtrue;
|
|
_printf( "will generate fakemap.map\n");
|
|
}
|
|
else if (!strcmp(argv[i], "-samplesize"))
|
|
{
|
|
samplesize = atoi(argv[i+1]);
|
|
if (samplesize < 1) samplesize = 1;
|
|
i++;
|
|
_printf("lightmap sample size is %dx%d units\n", samplesize, samplesize);
|
|
}
|
|
else if (argv[i][0] == '-')
|
|
Error ("Unknown option \"%s\"", argv[i]);
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (i != argc - 1)
|
|
Error ("usage: q3map [options] mapfile");
|
|
|
|
start = I_FloatTime ();
|
|
|
|
ThreadSetDefault ();
|
|
//numthreads = 1; // multiple threads aren't helping because of heavy malloc use
|
|
SetQdirFromPath (argv[i]);
|
|
|
|
#ifdef _WIN32
|
|
InitPakFile(gamedir, NULL);
|
|
#endif
|
|
|
|
strcpy (source, ExpandArg (argv[i]));
|
|
StripExtension (source);
|
|
|
|
// delete portal and line files
|
|
sprintf (path, "%s.prt", source);
|
|
remove (path);
|
|
sprintf (path, "%s.lin", source);
|
|
remove (path);
|
|
|
|
strcpy (name, ExpandArg (argv[i]));
|
|
if ( strcmp(name + strlen(name) - 4, ".reg" ) ) {
|
|
// if we are doing a full map, delete the last saved region map
|
|
sprintf (path, "%s.reg", source);
|
|
remove (path);
|
|
|
|
DefaultExtension (name, ".map"); // might be .reg
|
|
}
|
|
|
|
//
|
|
// if onlyents, just grab the entites and resave
|
|
//
|
|
if ( onlyents ) {
|
|
OnlyEnts();
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// if onlytextures, just grab the textures and resave
|
|
//
|
|
if ( onlytextures ) {
|
|
OnlyTextures();
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// start from scratch
|
|
//
|
|
LoadShaderInfo();
|
|
|
|
// load original file from temp spot in case it was renamed by the editor on the way in
|
|
if (strlen(tempsource) > 0) {
|
|
LoadMapFile (tempsource);
|
|
} else {
|
|
LoadMapFile (name);
|
|
}
|
|
|
|
SetModelNumbers ();
|
|
SetLightStyles ();
|
|
|
|
ProcessModels ();
|
|
|
|
EndBSPFile();
|
|
|
|
end = I_FloatTime ();
|
|
_printf ("%5.0f seconds elapsed\n", end-start);
|
|
|
|
// remove temp name if appropriate
|
|
if (strlen(tempsource) > 0) {
|
|
remove(tempsource);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|