/* =========================================================================== Copyright (C) 1997-2006 Id Software, Inc. This file is part of Quake 2 Tools source code. Quake 2 Tools 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 2 Tools 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 Quake 2 Tools source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "qbsp.h" extern float subdivide_size; char source[1024]; char name[1024]; vec_t microvolume = 1.0; qboolean noprune; qboolean glview; qboolean nodetail; qboolean fulldetail; qboolean onlyents; qboolean nomerge; qboolean nowater; qboolean nofill; qboolean nocsg; qboolean noweld; qboolean noshare; qboolean nosubdiv; qboolean notjunc; qboolean noopt; qboolean leaktest; qboolean verboseentities; char outbase[32]; int block_xl = -8, block_xh = 7, block_yl = -8, block_yh = 7; int entity_num; node_t *block_nodes[10][10]; /* ============ BlockTree ============ */ node_t *BlockTree (int xl, int yl, int xh, int yh) { node_t *node; vec3_t normal; float dist; int mid; if (xl == xh && yl == yh) { node = block_nodes[xl+5][yl+5]; if (!node) { // return an empty leaf node = AllocNode (); node->planenum = PLANENUM_LEAF; node->contents = 0; //CONTENTS_SOLID; return node; } return node; } // create a seperator along the largest axis node = AllocNode (); if (xh - xl > yh - yl) { // split x axis mid = xl + (xh-xl)/2 + 1; normal[0] = 1; normal[1] = 0; normal[2] = 0; dist = mid*1024; node->planenum = FindFloatPlane (normal, dist); node->children[0] = BlockTree ( mid, yl, xh, yh); node->children[1] = BlockTree ( xl, yl, mid-1, yh); } else { mid = yl + (yh-yl)/2 + 1; normal[0] = 0; normal[1] = 1; normal[2] = 0; dist = mid*1024; node->planenum = FindFloatPlane (normal, dist); node->children[0] = BlockTree ( xl, mid, xh, yh); node->children[1] = BlockTree ( xl, yl, xh, mid-1); } return node; } /* ============ ProcessBlock_Thread ============ */ int brush_start, brush_end; void ProcessBlock_Thread (int blocknum) { int xblock, yblock; vec3_t mins, maxs; bspbrush_t *brushes; tree_t *tree; node_t *node; yblock = block_yl + blocknum / (block_xh-block_xl+1); xblock = block_xl + blocknum % (block_xh-block_xl+1); qprintf ("############### block %2i,%2i ###############\n", xblock, yblock); mins[0] = xblock*1024; mins[1] = yblock*1024; mins[2] = -4096; maxs[0] = (xblock+1)*1024; maxs[1] = (yblock+1)*1024; maxs[2] = 4096; // the makelist and chopbrushes could be cached between the passes... brushes = MakeBspBrushList (brush_start, brush_end, mins, maxs); if (!brushes) { node = AllocNode (); node->planenum = PLANENUM_LEAF; node->contents = CONTENTS_SOLID; block_nodes[xblock+5][yblock+5] = node; return; } if (!nocsg) brushes = ChopBrushes (brushes); tree = BrushBSP (brushes, mins, maxs); block_nodes[xblock+5][yblock+5] = tree->headnode; } /* ============ ProcessWorldModel ============ */ void ProcessWorldModel (void) { entity_t *e; tree_t *tree; qboolean leaked; qboolean optimize; e = &entities[entity_num]; brush_start = e->firstbrush; brush_end = brush_start + e->numbrushes; leaked = false; // // perform per-block operations // if (block_xh * 1024 > map_maxs[0]) block_xh = floor(map_maxs[0]/1024.0); if ( (block_xl+1) * 1024 < map_mins[0]) block_xl = floor(map_mins[0]/1024.0); if (block_yh * 1024 > map_maxs[1]) block_yh = floor(map_maxs[1]/1024.0); if ( (block_yl+1) * 1024 < map_mins[1]) block_yl = floor(map_mins[1]/1024.0); if (block_xl <-4) block_xl = -4; if (block_yl <-4) block_yl = -4; if (block_xh > 3) block_xh = 3; if (block_yh > 3) block_yh = 3; for (optimize = false ; optimize <= true ; optimize++) { qprintf ("--------------------------------------------\n"); RunThreadsOnIndividual ((block_xh-block_xl+1)*(block_yh-block_yl+1), !verbose, ProcessBlock_Thread); // // build the division tree // oversizing the blocks guarantees that all the boundaries // will also get nodes. // qprintf ("--------------------------------------------\n"); tree = AllocTree (); tree->headnode = BlockTree (block_xl-1, block_yl-1, block_xh+1, block_yh+1); tree->mins[0] = (block_xl)*1024; tree->mins[1] = (block_yl)*1024; tree->mins[2] = map_mins[2] - 8; tree->maxs[0] = (block_xh+1)*1024; tree->maxs[1] = (block_yh+1)*1024; tree->maxs[2] = map_maxs[2] + 8; // // perform the global operations // MakeTreePortals (tree); if (FloodEntities (tree)) FillOutside (tree->headnode); else { printf ("**** leaked ****\n"); leaked = true; LeakFile (tree); if (leaktest) { printf ("--- MAP LEAKED ---\n"); exit (0); } } MarkVisibleSides (tree, brush_start, brush_end); if (noopt || leaked) break; if (!optimize) { FreeTree (tree); } } FloodAreas (tree); if (glview) WriteGLView (tree, source); MakeFaces (tree->headnode); FixTjuncs (tree->headnode); if (!noprune) PruneNodes (tree->headnode); WriteBSP (tree->headnode); if (!leaked) WritePortalFile (tree); FreeTree (tree); } /* ============ ProcessSubModel ============ */ 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); FreeTree (tree); } /* ============ ProcessModels ============ */ void ProcessModels (void) { BeginBSPFile (); for (entity_num=0 ; entity_num< num_entities ; entity_num++) { if (!entities[entity_num].numbrushes) continue; qprintf ("############### model %i ###############\n", nummodels); BeginModel (); if (entity_num == 0) ProcessWorldModel (); else ProcessSubModel (); EndModel (); if (!verboseentities) verbose = false; // don't bother printing submodels } EndBSPFile (); } /* ============ main ============ */ int main (int argc, char **argv) { int i; double start, end; char path[1024]; printf ("---- qbsp3 ----\n"); for (i=1 ; i<argc ; i++) { if (!strcmp(argv[i],"-threads")) { numthreads = atoi (argv[i+1]); i++; } else if (!strcmp(argv[i],"-glview")) { glview = true; } else if (!strcmp(argv[i], "-v")) { printf ("verbose = true\n"); verbose = true; } else if (!strcmp(argv[i], "-draw")) { printf ("drawflag = true\n"); drawflag = true; } else if (!strcmp(argv[i], "-noweld")) { printf ("noweld = true\n"); noweld = true; } else if (!strcmp(argv[i], "-nocsg")) { printf ("nocsg = true\n"); nocsg = true; } else if (!strcmp(argv[i], "-noshare")) { printf ("noshare = true\n"); noshare = true; } else if (!strcmp(argv[i], "-notjunc")) { printf ("notjunc = true\n"); notjunc = true; } else if (!strcmp(argv[i], "-nowater")) { printf ("nowater = true\n"); nowater = true; } else if (!strcmp(argv[i], "-noopt")) { printf ("noopt = true\n"); noopt = true; } else if (!strcmp(argv[i], "-noprune")) { printf ("noprune = true\n"); noprune = true; } else if (!strcmp(argv[i], "-nofill")) { printf ("nofill = true\n"); nofill = true; } else if (!strcmp(argv[i], "-nomerge")) { printf ("nomerge = true\n"); nomerge = true; } else if (!strcmp(argv[i], "-nosubdiv")) { printf ("nosubdiv = true\n"); nosubdiv = true; } else if (!strcmp(argv[i], "-nodetail")) { printf ("nodetail = true\n"); nodetail = true; } else if (!strcmp(argv[i], "-fulldetail")) { printf ("fulldetail = true\n"); fulldetail = true; } else if (!strcmp(argv[i], "-onlyents")) { printf ("onlyents = true\n"); onlyents = true; } else if (!strcmp(argv[i], "-micro")) { microvolume = atof(argv[i+1]); printf ("microvolume = %f\n", microvolume); i++; } else if (!strcmp(argv[i], "-leaktest")) { printf ("leaktest = true\n"); leaktest = true; } else if (!strcmp(argv[i], "-verboseentities")) { printf ("verboseentities = true\n"); verboseentities = true; } else if (!strcmp(argv[i], "-chop")) { subdivide_size = atof(argv[i+1]); printf ("subdivide_size = %f\n", subdivide_size); i++; } else if (!strcmp(argv[i], "-block")) { block_xl = block_xh = atoi(argv[i+1]); block_yl = block_yh = atoi(argv[i+2]); printf ("block: %i,%i\n", block_xl, block_yl); i+=2; } else if (!strcmp(argv[i], "-blocks")) { block_xl = atoi(argv[i+1]); block_yl = atoi(argv[i+2]); block_xh = atoi(argv[i+3]); block_yh = atoi(argv[i+4]); printf ("blocks: %i,%i to %i,%i\n", block_xl, block_yl, block_xh, block_yh); i+=4; } else if (!strcmp (argv[i],"-tmpout")) { strcpy (outbase, "/tmp"); } else if (argv[i][0] == '-') Error ("Unknown option \"%s\"", argv[i]); else break; } if (i != argc - 1) Error ("usage: qbsp3 [options] mapfile"); start = I_FloatTime (); ThreadSetDefault (); numthreads = 1; // multiple threads aren't helping... SetQdirFromPath (argv[i]); 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])); DefaultExtension (name, ".map"); // might be .reg // // if onlyents, just grab the entites and resave // if (onlyents) { char out[1024]; sprintf (out, "%s.bsp", source); LoadBSPFile (out); num_entities = 0; LoadMapFile (name); SetModelNumbers (); SetLightStyles (); UnparseEntities (); WriteBSPFile (out); } else { // // start from scratch // LoadMapFile (name); SetModelNumbers (); SetLightStyles (); ProcessModels (); } end = I_FloatTime (); printf ("%5.0f seconds elapsed\n", end-start); return 0; }