/* vis.c $Id: vis.c,v 1.12 2007/07/11 16:47:21 sezero Exp $ */ #include "util_inc.h" #include "cmdlib.h" #include "pathutil.h" #include "mathlib.h" #include "bspfile.h" #include "vis.h" #define MAX_THREADS 4 int GilMode = 1; int numportals, portalleafs; int c_portaltest, c_portalpass, c_portalcheck; portal_t *portals; leaf_t *leafs; int leafon; // the next leaf to be given // to a thread to process #ifdef __alpha # ifdef PLATFORM_WINDOWS HANDLE my_mutex; # else pthread_mutex_t *my_mutex; # endif /* windows */ #endif /* __alpha */ static byte *vismap, *vismap_p, *vismap_end; // past visfile static int originalvismapsize; byte *uncompressed; // [bitbytes*portalleafs] int bitbytes; // (portalleafs+63)>>3 int bitlongs; #ifdef __alpha int numthreads = 4; #else int numthreads = 1; #endif static qboolean fastvis; qboolean verbose; int testlevel = 2; #if 0 void NormalizePlane (plane_t *dp) { double ax, ay, az; if (dp->normal[0] == -1.0) { dp->normal[0] = 1.0; dp->dist = -dp->dist; return; } if (dp->normal[1] == -1.0) { dp->normal[1] = 1.0; dp->dist = -dp->dist; return; } if (dp->normal[2] == -1.0) { dp->normal[2] = 1.0; dp->dist = -dp->dist; return; } ax = fabs(dp->normal[0]); ay = fabs(dp->normal[1]); az = fabs(dp->normal[2]); if (ax >= ay && ax >= az) { if (dp->normal[0] < 0) { VectorNegate (dp->normal, dp->normal); dp->dist = -dp->dist; } return; } if (ay >= ax && ay >= az) { if (dp->normal[1] < 0) { VectorNegate (dp->normal, dp->normal); dp->dist = -dp->dist; } return; } if (dp->normal[2] < 0) { VectorNegate (dp->normal, dp->normal); dp->dist = -dp->dist; } } #endif static void PlaneFromWinding (winding_t *w, plane_t *plane) { vec3_t v1, v2; // calc plane VectorSubtract (w->points[2], w->points[1], v1); VectorSubtract (w->points[0], w->points[1], v2); CrossProduct (v2, v1, plane->normal); VectorNormalize (plane->normal); plane->dist = (float) DotProduct (w->points[0], plane->normal); } //============================================================================= /* ================== NewWinding ================== */ #define MAX_FREE_WINDINGS (10000) #define MAX_FAST_ALLOC_WINDINGS (16) #ifdef PLATFORM_WINDOWS static winding_t *OldNewWinding (int points) { winding_t *w; int size; if (points > MAX_POINTS_ON_WINDING) Error ("%s: %i points", __thisfunc__, points); size = (int)((winding_t *)0)->points[points]; w = (winding_t *) malloc (size); // memset (w, 0, size); w->fixedsize = 0; w->original = 0; w->numpoints = 0; return w; } static void OldFreeWinding (winding_t *w) { if (!w->original) free (w); } __declspec( thread ) static int nFreeWindings = 0; __declspec( thread ) static winding_t *FreeWindings[MAX_FREE_WINDINGS]; winding_t *NewWinding (int points) { winding_t *ret; if (points > MAX_FAST_ALLOC_WINDINGS || !GilMode) { ret = OldNewWinding(points); ret->fixedsize = 0; } else if (nFreeWindings) ret = FreeWindings[--nFreeWindings]; else { ret = OldNewWinding(MAX_FAST_ALLOC_WINDINGS); ret->fixedsize = 1; } ret->original = 0; ret->numpoints = 0; return ret; } void FreeWinding (winding_t *w) { if (!w->original) { if (nFreeWindings >= MAX_FREE_WINDINGS || !w->fixedsize) OldFreeWinding(w); else FreeWindings[nFreeWindings++] = w; } } #else /* old way */ winding_t *NewWinding (int points) { winding_t *w; size_t size; if (points > MAX_POINTS_ON_WINDING) Error ("%s: %i points", __thisfunc__, points); size = (size_t)((winding_t *)0)->points[points]; w = (winding_t *) malloc (size); memset (w, 0, size); return w; } void FreeWinding (winding_t *w) { if (!w->original) free (w); } #endif #if 0 // not used void pw (winding_t *w) { int i; for (i = 0 ; i < w->numpoints ; i++) printf ("(%5.1f, %5.1f, %5.1f)\n",w->points[i][0], w->points[i][1],w->points[i][2]); } void prl(leaf_t *l) { int i; portal_t *p; plane_t pl; for (i = 0 ; i < l->numportals ; i++) { p = l->portals[i]; pl = p->plane; printf ("portal %4i to leaf %4i : %7.1f : (%4.1f, %4.1f, %4.1f)\n",(int)(p-portals),p->leaf,pl.dist, pl.normal[0], pl.normal[1], pl.normal[2]); } } #endif /* ================== CopyWinding ================== */ winding_t *CopyWinding (winding_t *w) { size_t size; winding_t *c; size = (size_t)((winding_t *)0)->points[w->numpoints]; // c = (winding_t *) malloc (size); c = NewWinding(w->numpoints); memcpy (c, w, size); c->original = false; return c; } /* ================== ClipWinding Clips the winding to the plane, returning the new winding on the positive side Frees the input winding. If keepon is true, an exactly on-plane winding will be saved, otherwise it will be clipped away. ================== */ winding_t *ClipWinding (winding_t *in, plane_t *split, qboolean keepon) { double dists[MAX_POINTS_ON_WINDING]; int sides[MAX_POINTS_ON_WINDING]; int counts[3]; double dot; int i, j; double *p1, *p2; vec3_t mid; winding_t *neww; int maxpts; counts[0] = counts[1] = counts[2] = 0; // determine sides for each point for (i = 0 ; i < in->numpoints ; i++) { dot = DotProduct (in->points[i], split->normal); dot -= split->dist; dists[i] = dot; if (dot > ON_EPSILON) sides[i] = SIDE_FRONT; else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK; else { sides[i] = SIDE_ON; } counts[sides[i]]++; } sides[i] = sides[0]; dists[i] = dists[0]; if (keepon && !counts[0] && !counts[1]) return in; if (!counts[0]) { FreeWinding (in); return NULL; } if (!counts[1]) return in; maxpts = in->numpoints + 4; // can't use counts[0]+2 because // of fp grouping errors neww = NewWinding (maxpts); for (i = 0 ; i < in->numpoints ; i++) { p1 = in->points[i]; if (sides[i] == SIDE_ON) { VectorCopy (p1, neww->points[neww->numpoints]); neww->numpoints++; continue; } if (sides[i] == SIDE_FRONT) { VectorCopy (p1, neww->points[neww->numpoints]); neww->numpoints++; } if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) continue; // generate a split point p2 = in->points[(i+1)%in->numpoints]; dot = dists[i] / (dists[i]-dists[i+1]); for (j = 0 ; j < 3 ; j++) { // avoid round off error when possible if (split->normal[j] == 1) mid[j] = split->dist; else if (split->normal[j] == -1) mid[j] = -split->dist; else mid[j] = p1[j] + dot*(p2[j]-p1[j]); } VectorCopy (mid, neww->points[neww->numpoints]); neww->numpoints++; } if (neww->numpoints > maxpts) Error ("%s: points exceeded estimate", __thisfunc__); // free the original winding FreeWinding (in); return neww; } //============================================================================= /* ============= GetNextPortal Returns the next portal for a thread to work on Returns the portals from the least complex, so the later ones can reuse the earlier information. ============= */ #if 1 //static int maxpercent=0; static portal_t *GetNextPortal (void) { int j; portal_t *p, *tp; int min; // int ndone; const int num2 = numportals*2; min = 99999; p = NULL; LOCK; // ndone = 0; for (j = 0, tp = portals ; j < num2 ; j++, tp++) { if (tp->nummightsee < min && tp->status == stat_none) { min = tp->nummightsee; p = tp; } // else if (tp->status == stat_done) // ndone++; } if (p) p->status = stat_working; UNLOCK; /* if (GilMode) { ndone *= 50; ndone /= numportals; if (ndone-1 > maxpercent) { maxpercent = ndone; printf("%d %% done\t", ndone); } } */ return p; } #else static portal_t *GetNextPortal (void) { int j; portal_t *p, *tp; int min; LOCK; min = -99999; p = NULL; for (j = 0, tp = portals ; j < numportals*2 ; j++, tp++) { if (tp->nummightsee > min && tp->status == stat_none) { min = tp->nummightsee; p = tp; } } if (p) p->status = stat_working; UNLOCK; return p; } #endif /* ============== LeafThread ============== */ #ifdef __alpha # ifdef PLATFORM_WINDOWS static LPVOID LeafThread (LPVOID thread) # else static pthread_addr_t LeafThread (pthread_addr_t thread) # endif #else static void *LeafThread (int thread) #endif { portal_t *p; printf ("Begining %s: %i\n", __thisfunc__, (int)thread); do { p = GetNextPortal (); if (!p) break; PortalFlow (p); if (verbose) printf ("portal:%4i mightsee:%4i cansee:%4i\n", (int)(p - portals), p->nummightsee, p->numcansee); } while (1); printf ("Completed %s: %i\n", __thisfunc__, (int)thread); return NULL; } /* =============== CompressRow =============== */ static int CompressRow (byte *vis, byte *dest) { int j; int rep; int visrow; byte *dest_p; dest_p = dest; visrow = (portalleafs + 7)>>3; for (j = 0 ; j < visrow ; j++) { *dest_p++ = vis[j]; if (vis[j]) continue; rep = 1; for (j++; j < visrow ; j++) { if (vis[j] || rep == 255) break; else rep++; } *dest_p++ = rep; j--; } return dest_p - dest; } /* =============== LeafFlow Builds the entire visibility list for a leaf =============== */ static int totalvis; static void LeafFlow (int leafnum) { leaf_t *leaf; byte *outbuffer; byte compressed[MAX_MAP_LEAFS/8]; int i, j; int numvis; byte *dest; portal_t *p; // // flow through all portals, collecting visible bits // outbuffer = uncompressed + leafnum*bitbytes; leaf = &leafs[leafnum]; for (i = 0 ; i < leaf->numportals ; i++) { p = leaf->portals[i]; if (p->status != stat_done) Error ("portal not done"); for (j = 0 ; j < bitbytes ; j++) outbuffer[j] |= p->visbits[j]; } if (outbuffer[leafnum>>3] & (1<<(leafnum&7))) Error ("Leaf portals saw into leaf"); outbuffer[leafnum>>3] |= (1<<(leafnum&7)); numvis = 0; for (i = 0 ; i < portalleafs ; i++) { if ( outbuffer[i>>3] & (1<<(i & 3)) ) numvis++; } // // compress the bit string // if (verbose) printf ("leaf %4i : %4i visible\n", leafnum, numvis); totalvis += numvis; #if 0 i = (portalleafs + 7) >> 3; memcpy (compressed, outbuffer, i); #else i = CompressRow (outbuffer, compressed); #endif dest = vismap_p; vismap_p += i; if (vismap_p > vismap_end) Error ("Vismap expansion overflow"); dleafs[leafnum+1].visofs = dest-vismap; // leaf 0 is a common solid memcpy (dest, compressed, i); } /* ================== CalcPortalVis ================== */ static void CalcPortalVis (void) { int i; // fastvis just uses mightsee for a very loose bound if (fastvis) { for (i = 0 ; i < numportals*2 ; i++) { portals[i].visbits = portals[i].mightsee; portals[i].status = stat_done; } return; } leafon = 0; #ifdef __alpha # ifdef PLATFORM_WINDOWS { DWORD IDThread; HANDLE work_threads[MAX_THREADS]; int i; my_mutex = CreateMutex(NULL, FALSE, NULL); // cleared for (i = 0 ; i < numthreads-1 ; i++) { work_threads[i] = CreateThread( NULL, // no security attrib 0x100000, // stack size (LPTHREAD_START_ROUTINE) LeafThread, // thread function (LPVOID) i, // thread function arg 0, // use default creation flags &IDThread ); if (work_threads[i] == NULL) Error ("pthread_create failed"); } LeafThread((LPVOID)(numthreads-1)); for (i = 0 ; i < numthreads-1 ; i++) { WaitForSingleObject(work_threads[i], INFINITE); } CloseHandle (my_mutex); } # else { pthread_t work_threads[MAX_THREADS]; pthread_addr_t status; pthread_attr_t attrib; pthread_mutexattr_t mattrib; int i; my_mutex = (pthread_mutex_t *) malloc (sizeof(*my_mutex)); if (pthread_mutexattr_create (&mattrib) == -1) Error ("pthread_mutex_attr_create failed"); if (pthread_mutexattr_setkind_np (&mattrib, MUTEX_FAST_NP) == -1) Error ("pthread_mutexattr_setkind_np failed"); if (pthread_mutex_init (my_mutex, mattrib) == -1) Error ("pthread_mutex_init failed"); if (pthread_attr_create (&attrib) == -1) Error ("pthread_attr_create failed"); if (pthread_attr_setstacksize (&attrib, 0x100000) == -1) Error ("pthread_attr_setstacksize failed"); for (i = 0 ; i < numthreads ; i++) { if (pthread_create(&work_threads[i], attrib, LeafThread, (pthread_addr_t)i) == -1) Error ("pthread_create failed"); } for (i = 0 ; i < numthreads ; i++) { if (pthread_join (work_threads[i], &status) == -1) Error ("pthread_join failed"); } if (pthread_mutex_destroy (my_mutex) == -1) Error ("pthread_mutex_destroy failed"); } # endif /* windows */ #else /* ! __alpha */ LeafThread (0); #endif /* __alpha */ if (verbose) { printf ("portalcheck: %i portaltest: %i portalpass: %i\n",c_portalcheck, c_portaltest, c_portalpass); printf ("c_vistest: %i c_mighttest: %i\n",c_vistest, c_mighttest); } } /* ================== CalcVis ================== */ static void CalcVis (void) { int i; BasePortalVis (); CalcPortalVis (); // // assemble the leaf vis lists by oring and compressing the portal lists // for (i = 0 ; i < portalleafs ; i++) LeafFlow (i); printf ("average leafs visible: %i\n", totalvis / portalleafs); } /* ============================================================================== PASSAGE CALCULATION (not used yet...) ============================================================================== */ #if 0 int count_sep; qboolean PlaneCompare (plane_t *p1, plane_t *p2) { int i; if ( fabs(p1->dist - p2->dist) > 0.01) return false; for (i = 0 ; i < 3 ; i++) { if ( fabs(p1->normal[i] - p2->normal[i] ) > 0.001) return false; } return true; } sep_t *Findpassages (winding_t *source, winding_t *pass) { int i, j, k, l; plane_t plane; vec3_t v1, v2; float d; double length; int counts[3]; qboolean fliptest; sep_t *sep, *list; list = NULL; // check all combinations for (i = 0 ; i < source->numpoints ; i++) { l = (i+1)%source->numpoints; VectorSubtract (source->points[l] , source->points[i], v1); // fing a vertex of pass that makes a plane that puts all of the // vertexes of pass on the front side and all of the vertexes of // source on the back side for (j = 0 ; j < pass->numpoints ; j++) { VectorSubtract (pass->points[j], source->points[i], v2); plane.normal[0] = v1[1]*v2[2] - v1[2]*v2[1]; plane.normal[1] = v1[2]*v2[0] - v1[0]*v2[2]; plane.normal[2] = v1[0]*v2[1] - v1[1]*v2[0]; // if points don't make a valid plane, skip it length = plane.normal[0] * plane.normal[0] + plane.normal[1] * plane.normal[1] + plane.normal[2] * plane.normal[2]; if (length < ON_EPSILON) continue; length = 1 / sqrt(length); plane.normal[0] *= length; plane.normal[1] *= length; plane.normal[2] *= length; plane.dist = (float) DotProduct (pass->points[j], plane.normal); // // find out which side of the generated seperating plane has the // source portal // fliptest = false; for (k = 0 ; k < source->numpoints ; k++) { if (k == i || k == l) continue; d = (float) DotProduct (source->points[k], plane.normal) - plane.dist; if (d < -ON_EPSILON) { // source is on the negative side, so we want all // pass and target on the positive side fliptest = false; break; } else if (d > ON_EPSILON) { // source is on the positive side, so we want all // pass and target on the negative side fliptest = true; break; } } if (k == source->numpoints) continue; // planar with source portal // // flip the normal if the source portal is backwards // if (fliptest) { VectorNegate (plane.normal, plane.normal); plane.dist = -plane.dist; } // // if all of the pass portal points are now on the positive side, // this is the seperating plane // counts[0] = counts[1] = counts[2] = 0; for (k = 0 ; k < pass->numpoints ; k++) { if (k == j) continue; d = (float) DotProduct (pass->points[k], plane.normal) - plane.dist; if (d < -ON_EPSILON) break; else if (d > ON_EPSILON) counts[0]++; else counts[2]++; } if (k != pass->numpoints) continue; // points on negative side, not a seperating plane if (!counts[0]) continue; // planar with pass portal // // save this out // count_sep++; sep = (sep_t *) malloc(sizeof(*sep)); sep->next = list; list = sep; sep->plane = plane; } } return list; } #endif //============================================================================= /* ============ LoadPortals ============ */ static void LoadPortals (char *name) { int i, j; portal_t *p; leaf_t *l; char magic[80]; FILE *f; int numpoints; winding_t *w; int leafnums[2]; plane_t plane; if (!strcmp(name,"-")) f = stdin; else { f = fopen(name, "r"); if (!f) { printf ("%s: couldn't read %s\n", __thisfunc__, name); printf ("No vising performed.\n"); exit (1); } } if (fscanf (f,"%79s\n%i\n%i\n",magic, &portalleafs, &numportals) != 3) Error ("%s: failed to read header", __thisfunc__); if (strcmp(magic,PORTALFILE)) Error ("%s: not a portal file", __thisfunc__); printf ("%4i portalleafs\n", portalleafs); printf ("%4i numportals\n", numportals); bitbytes = ((portalleafs + 63) & ~63) >> 3; bitlongs = bitbytes / sizeof(long); // each file portal is split into two memory portals portals = (portal_t *) malloc(2 * numportals * sizeof(portal_t)); memset (portals, 0, 2 * numportals * sizeof(portal_t)); leafs = (leaf_t *) malloc(portalleafs * sizeof(leaf_t)); memset (leafs, 0, portalleafs * sizeof(leaf_t)); originalvismapsize = portalleafs*((portalleafs+7)/8); vismap = vismap_p = dvisdata; vismap_end = vismap + MAX_MAP_VISIBILITY; for (i = 0, p = portals ; i < numportals ; i++) { if (fscanf (f, "%i %i %i ", &numpoints, &leafnums[0], &leafnums[1]) != 3) Error ("%s: Error reading portal %i", __thisfunc__, i); if (numpoints > MAX_POINTS_ON_WINDING) Error ("%s: portal %i has too many points", __thisfunc__, i); if ( leafnums[0] > portalleafs || leafnums[1] > portalleafs) Error ("%s: portal %i, leafnums > portalleafs", __thisfunc__, i); w = p->winding = NewWinding (numpoints); w->original = true; w->numpoints = numpoints; for (j = 0 ; j < numpoints ; j++) { if (fscanf (f, "(%lf %lf %lf ) ", &w->points[j][0], &w->points[j][1], &w->points[j][2]) != 3) Error ("%s: reading portal %i", __thisfunc__, i); } fscanf (f, "\n"); // calc plane PlaneFromWinding (w, &plane); // create forward portal l = &leafs[leafnums[0]]; if (l->numportals == MAX_PORTALS_ON_LEAF) Error ("Leaf with too many portals"); l->portals[l->numportals] = p; l->numportals++; p->winding = w; VectorNegate (plane.normal, p->plane.normal); p->plane.dist = -plane.dist; p->leaf = leafnums[1]; p++; // create backwards portal l = &leafs[leafnums[1]]; if (l->numportals == MAX_PORTALS_ON_LEAF) Error ("Leaf with too many portals"); l->portals[l->numportals] = p; l->numportals++; p->winding = w; p->plane = plane; p->leaf = leafnums[0]; p++; } fclose (f); } /* =========== main =========== */ int main (int argc, char **argv) { char portalfile[1024]; char source[1024]; int i; double start, end; printf ("---- vis ----\n"); for (i = 1 ; i < argc ; i++) { if (!strcmp(argv[i],"-threads")) { numthreads = atoi (argv[i+1]); i++; } else if (!strcmp(argv[i], "-nogil")) { printf ("Gil's optimizations disabled\n"); GilMode = 0; } else if (!strcmp(argv[i], "-fast")) { printf ("fastvis = true\n"); fastvis = true; } else if (!strcmp(argv[i], "-level")) { testlevel = atoi(argv[i+1]); printf ("testlevel = %i\n", testlevel); i++; } else if (!strcmp(argv[i], "-v")) { printf ("verbose = true\n"); verbose = true; } else if (argv[i][0] == '-') Error ("Unknown option \"%s\"", argv[i]); else break; } if (i != argc - 1) Error ("usage: vis [-nogil] [-threads #] [-level 0-4] [-fast] [-v] bspfile"); start = GetTime (); strcpy (source, argv[i]); StripExtension (source); DefaultExtension (source, ".bsp"); LoadBSPFile (source); strcpy (portalfile, argv[i]); StripExtension (portalfile); strcat (portalfile, ".prt"); LoadPortals (portalfile); uncompressed = (byte *) malloc(bitbytes*portalleafs); memset (uncompressed, 0, bitbytes*portalleafs); CalcVis (); printf ("c_chains: %i\n", c_chains); visdatasize = vismap_p - dvisdata; printf ("visdatasize:%i compressed from %i\n", visdatasize, originalvismapsize); CalcAmbientSounds (); WriteBSPFile (source); // unlink (portalfile); if (GilMode) PrintStats(); end = GetTime (); printf ("%5.1f seconds elapsed\n", end-start); return 0; }