/* Copyright (C) 1996-1997 Id Software, Inc. This program 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. This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA See file, 'COPYING', for details. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif static __attribute__ ((used)) const char rcsid[] = "$Id$"; #include #ifdef HAVE_SYS_WAIT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_MALLOC_H #include #endif #include #include #include "QF/quakefs.h" #include "QF/sys.h" #include "csg4.h" #include "brush.h" #include "bsp5.h" #include "merge.h" #include "options.h" #include "outside.h" #include "portals.h" #include "readbsp.h" #include "solidbsp.h" #include "surfaces.h" #include "writebsp.h" #include "tjunc.h" options_t options; bsp_t *bsp; brushset_t *brushset; int c_activefaces, c_peakfaces; int c_activesurfaces, c_peaksurfaces; int c_activeportals, c_peakportals; int valid; char *argv0; // changed after fork(); qboolean worldmodel; int hullnum; face_t * AllocFace (void) { face_t *f; c_activefaces++; if (c_activefaces > c_peakfaces) c_peakfaces = c_activefaces; f = malloc (sizeof (face_t)); memset (f, 0, sizeof (face_t)); f->planenum = -1; return f; } void FreeFace (face_t *f) { c_activefaces--; free (f); } surface_t * AllocSurface (void) { surface_t *s; s = malloc (sizeof (surface_t)); memset (s, 0, sizeof (surface_t)); c_activesurfaces++; if (c_activesurfaces > c_peaksurfaces) c_peaksurfaces = c_activesurfaces; return s; } void FreeSurface (surface_t *s) { c_activesurfaces--; free (s); } portal_t * AllocPortal (void) { portal_t *p; c_activeportals++; if (c_activeportals > c_peakportals) c_peakportals = c_activeportals; p = malloc (sizeof (portal_t)); memset (p, 0, sizeof (portal_t)); return p; } void FreePortal (portal_t *p) { c_activeportals--; free (p); } node_t * AllocNode (void) { node_t *n; n = malloc (sizeof (node_t)); memset (n, 0, sizeof (node_t)); return n; } brush_t * AllocBrush (void) { brush_t *b; b = malloc (sizeof (brush_t)); memset (b, 0, sizeof (brush_t)); return b; } static void ProcessEntity (int entnum) { brushset_t *bs; char mod[80]; entity_t *ent; surface_t *surfs; node_t *nodes; ent = &entities[entnum]; if (!ent->brushes) return; // non-bmodel entity if (entnum > 0) { worldmodel = false; if (entnum == 1) qprintf ("--- Internal Entities ---\n"); sprintf (mod, "*%i", bsp->nummodels); if (options.verbosity) PrintEntity (ent); if (hullnum == 0) printf ("MODEL: %s\n", mod); SetKeyValue (ent, "model", mod); } else worldmodel = true; // take the brush_ts and clip off all overlapping and contained faces, // leaving a perfect skin of the model with no hidden faces bs = Brush_LoadEntity (ent, hullnum); if (!bs->brushes) { PrintEntity (ent); Sys_Error ("Entity with no valid brushes"); } brushset = bs; surfs = CSGFaces (bs); if (hullnum != 0) { nodes = SolidBSP (surfs, true); if (entnum == 0 && !options.nofill) { // assume non-world bmodels are simple PortalizeWorld (nodes); if (FillOutside (nodes)) { surfs = GatherNodeFaces (nodes); nodes = SolidBSP (surfs, false); // make a really good tree } FreeAllPortals (nodes); } WriteNodePlanes (nodes); WriteClipNodes (nodes); BumpModel (hullnum); } else { // SolidBSP generates a node tree // // if not the world, make a good tree first // the world is just going to make a bad tree // because the outside filling will force a regeneration later nodes = SolidBSP (surfs, entnum == 0); // build all the portals in the bsp tree // some portals are solid polygons, and some are paths to other leafs if (entnum == 0 && !options.nofill) { // assume non-world bmodels are simple PortalizeWorld (nodes); if (FillOutside (nodes)) { FreeAllPortals (nodes); // get the remaining faces together into surfaces again surfs = GatherNodeFaces (nodes); // merge polygons MergeAll (surfs); // make a really good tree nodes = SolidBSP (surfs, false); // make the real portals for vis tracing PortalizeWorldDetail (nodes); // save portal file for vis tracing WritePortalfile (nodes); // fix tjunctions tjunc (nodes); } FreeAllPortals (nodes); } WriteNodePlanes (nodes); MakeFaceEdges (nodes); WriteDrawNodes (nodes); } } static void UpdateEntLump (void) { int m, entnum; char mod[80]; QFile *f; m = 1; for (entnum = 1; entnum < num_entities; entnum++) { if (!entities[entnum].brushes) continue; sprintf (mod, "*%i", m); SetKeyValue (&entities[entnum], "model", mod); m++; } printf ("Updating entities lump...\n"); f = Qopen (options.bspfile, "rb"); if (!f) Sys_Error ("couldn't open %s. %s", options.bspfile, strerror(errno)); bsp = LoadBSPFile (f, Qfilesize (f)); Qclose (f); WriteEntitiesToString (); f = Qopen (options.bspfile, "wb"); if (!f) Sys_Error ("couldn't open %s. %s", options.bspfile, strerror(errno)); WriteBSPFile (bsp, f); Qclose (f); } /* WriteClipHull Write the clipping hull out to a text file so the parent process can get it */ static void WriteClipHull (void) { FILE *f; dclipnode_t *d; dplane_t *p; int i; options.hullfile[strlen (options.hullfile) - 1] = '0' + hullnum; qprintf ("---- WriteClipHull ----\n"); qprintf ("Writing %s\n", options.hullfile); f = fopen (options.hullfile, "w"); if (!f) Sys_Error ("Couldn't open %s", options.hullfile); fprintf (f, "%i\n", bsp->nummodels); for (i = 0; i < bsp->nummodels; i++) fprintf (f, "%i\n", bsp->models[i].headnode[hullnum]); fprintf (f, "\n%i\n", bsp->numclipnodes); for (i = 0; i < bsp->numclipnodes; i++) { d = &bsp->clipnodes[i]; p = &bsp->planes[d->planenum]; // the node number is written out only for human readability fprintf (f, "%5i : %f %f %f %f : %5i %5i\n", i, p->normal[0], p->normal[1], p->normal[2], p->dist, d->children[0], d->children[1]); } fclose (f); } /* ReadClipHull Read the files written out by the child processes */ static void ReadClipHull (int hullnum) { FILE *f; dclipnode_t d; dplane_t p; float f1, f2, f3, f4; int firstclipnode, junk, c1, c2, i, j, n; vec3_t norm; options.hullfile[strlen (options.hullfile) - 1] = '0' + hullnum; f = fopen (options.hullfile, "r"); if (!f) Sys_Error ("Couldn't open %s", options.hullfile); if (fscanf (f, "%d\n", &n) != 1) Sys_Error ("Error parsing %s", options.hullfile); if (n != bsp->nummodels) Sys_Error ("ReadClipHull: hull had %i models, base had %i", n, bsp->nummodels); for (i = 0; i < n; i++) { if (fscanf (f, "%d\n", &j) != 1) Sys_Error ("Error parsing %s", options.hullfile); bsp->models[i].headnode[hullnum] = bsp->numclipnodes + j; } if (fscanf (f, "\n%d\n", &n) != 1) Sys_Error ("Error parsing %s", options.hullfile); firstclipnode = bsp->numclipnodes; for (i = 0; i < n; i++) { if (bsp->numclipnodes == MAX_MAP_CLIPNODES) Sys_Error ("ReadClipHull: MAX_MAP_CLIPNODES"); if (fscanf (f, "%d : %f %f %f %f : %d %d\n", &junk, &f1, &f2, &f3, &f4, &c1, &c2) != 7) Sys_Error ("Error parsing %s", options.hullfile); p.normal[0] = f1; p.normal[1] = f2; p.normal[2] = f3; p.dist = f4; norm[0] = f1; norm[1] = f2; norm[2] = f3; // vec_t precision p.type = PlaneTypeForNormal (norm); d.children[0] = c1 >= 0 ? c1 + firstclipnode : c1; d.children[1] = c2 >= 0 ? c2 + firstclipnode : c2; d.planenum = FindFinalPlane (&p); BSP_AddClipnode (bsp, &d); } } static void CreateSingleHull (void) { int entnum; // for each entity in the map file that has geometry for (entnum = 0; entnum < num_entities; entnum++) { ProcessEntity (entnum); if (options.verbosity < 2) options.verbosity = 0; // don't print rest of entities } if (hullnum) WriteClipHull (); } static void CreateHulls (void) { // commanded to create only a single hull if (hullnum) { CreateSingleHull (); exit (0); } // commanded to use the allready existing hulls 1 and 2 if (options.usehulls) { CreateSingleHull (); return; } // commanded to ignore the hulls altogether if (options.noclip) { CreateSingleHull (); return; } // create all the hulls #ifdef __alpha printf ("forking hull processes...\n"); // fork a process for each clipping hull fflush (stdout); if (!fork ()) { hullnum = 1; options.verbosity = 0; options.drawflag = false; sprintf (argv0, "HUL%i", hullnum); } else if (!fork ()) { hullnum = 2; options.verbosity = 0; options.drawflag = false; sprintf (argv0, "HUL%i", hullnum); } CreateSingleHull (); if (hullnum) exit (0); wait (NULL); // wait for clip hull process to finish wait (NULL); // wait for clip hull process to finish #else // create the hulls sequentially printf ("building hulls sequentially...\n"); hullnum = 1; CreateSingleHull (); bsp->nummodels = 0; bsp->numplanes = 0; bsp->numclipnodes = 0; hullnum = 2; CreateSingleHull (); bsp->nummodels = 0; bsp->numplanes = 0; bsp->numclipnodes = 0; hullnum = 0; CreateSingleHull (); #endif } static void ProcessFile (void) { bsp = BSP_New (); if (options.extract) { LoadBSP (); if (options.portal) bsp2prt (); if (options.extract_textures) extract_textures (); if (options.extract_entities) extract_entities (); if (options.extract_hull) extract_hull (); return; } // load brushes and entities LoadMapFile (options.mapfile); if (options.onlyents) { UpdateEntLump (); return; } remove (options.bspfile); if (!options.usehulls) { options.hullfile[strlen (options.hullfile) - 1] = '1'; remove (options.hullfile); options.hullfile[strlen (options.hullfile) - 1] = '2'; remove (options.hullfile); } remove (options.portfile); remove (options.pointfile); // init the tables to be shared by all models BeginBSPFile (); // the clipping hulls will be written out to text files by forked processes CreateHulls (); ReadClipHull (1); ReadClipHull (2); WriteEntitiesToString (); FinishBSPFile (); } int main (int argc, char **argv) { double start, end; // let forked processes change name for ps status this_program = argv0 = argv[0]; // check command line flags DecodeArgs (argc, argv); // XXX SetQdirFromPath (argv[i]); // do it! start = Sys_DoubleTime (); ProcessFile (); end = Sys_DoubleTime (); printf ("%5.1f seconds elapsed\n", end - start); return 0; } void qprintf (const char *fmt, ...) { va_list argptr; if (!options.verbosity) return; va_start (argptr, fmt); vprintf (fmt, argptr); va_end (argptr); }