// vis.c #include "vis.h" #define MAX_THREADS 4 int numportals; int portalleafs; portal_t *portals; leaf_t *leafs; int c_portaltest, c_portalpass, c_portalcheck; qboolean showgetleaf = true; int leafon; // the next leaf to be given to a thread to process #ifdef __alpha pthread_mutex_t *my_mutex; #endif byte *vismap, *vismap_p, *vismap_end; // past visfile int originalvismapsize; byte *uncompressed; // [bitbytes*portalleafs] int bitbytes; // (portalleafs+63)>>3 int bitlongs; #ifdef __alpha int numthreads = 4; #else int numthreads = 1; #endif qboolean fastvis; qboolean verbose; int testlevel = 2; #if 0 void NormalizePlane (plane_t *dp) { vec_t 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) { VectorSubtract (vec3_origin, dp->normal, dp->normal); dp->dist = -dp->dist; } return; } if (ay >= ax && ay >= az) { if (dp->normal[1] < 0) { VectorSubtract (vec3_origin, dp->normal, dp->normal); dp->dist = -dp->dist; } return; } if (dp->normal[2] < 0) { VectorSubtract (vec3_origin, dp->normal, dp->normal); dp->dist = -dp->dist; } } #endif 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 = DotProduct (w->points[0], plane->normal); } //============================================================================= /* ================== NewWinding ================== */ winding_t *NewWinding (int points) { winding_t *w; int size; if (points > MAX_POINTS_ON_WINDING) Error ("NewWinding: %i points", points); size = (int)((winding_t *)0)->points[points]; w = malloc (size); memset (w, 0, size); return w; } void FreeWinding (winding_t *w) { if (!w->original) free (w); } void pw(winding_t *w) { int i; for (i=0 ; inumpoints ; 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 ; inumportals ; 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]); } } /* ================== CopyWinding ================== */ winding_t *CopyWinding (winding_t *w) { int size; winding_t *c; size = (int)((winding_t *)0)->points[w->numpoints]; c = malloc (size); 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) { vec_t dists[MAX_POINTS_ON_WINDING]; int sides[MAX_POINTS_ON_WINDING]; int counts[3]; vec_t dot; int i, j; vec_t *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 ; inumpoints ; 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 ; inumpoints ; 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 ("ClipWinding: points exceeded estimate"); // 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. ============= */ portal_t *GetNextPortal (void) { int j; portal_t *p, *tp; int min; LOCK; min = 99999; p = NULL; for (j=0, tp = portals ; jnummightsee < min && tp->status == stat_none) { min = tp->nummightsee; p = tp; } } if (p) p->status = stat_working; UNLOCK; return p; } /* ============== LeafThread ============== */ #ifdef __alpha pthread_addr_t LeafThread (pthread_addr_t thread) #else void *LeafThread (int thread) #endif { portal_t *p; //printf ("Begining LeafThread: %i\n",(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 LeafThread: %i\n",(int)thread); return NULL; } /* =============== CompressRow =============== */ 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 ; jnumportals ; i++) { p = leaf->portals[i]; if (p->status != stat_done) Error ("portal not done"); for (j=0 ; jvisbits[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>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 ================== */ void CalcPortalVis (void) { int i; // fastvis just uses mightsee for a very loose bound if (fastvis) { for (i=0 ; idist - 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 ; inumpoints ; 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 ; jnumpoints ; 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 = DotProduct (pass->points[j], plane.normal); // // find out which side of the generated seperating plane has the // source portal // fliptest = false; for (k=0 ; knumpoints ; k++) { if (k == i || k == l) continue; d = 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) { VectorSubtract (vec3_origin, 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 ; knumpoints ; k++) { if (k==j) continue; d = 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 = malloc(sizeof(*sep)); sep->next = list; list = sep; sep->plane = plane; } } return list; } /* ============ CalcPassages ============ */ void CalcPassages (void) { int i, j, k; int count, count2; leaf_t *l; portal_t *p1, *p2; sep_t *sep; passage_t *passages; printf ("building passages...\n"); count = count2 = 0; for (i=0 ; inumportals ; j++) { p1 = l->portals[j]; for (k=0 ; knumportals ; k++) { if (k==j) continue; count++; p2 = l->portals[k]; // definately can't see into a coplanar portal if (PlaneCompare (&p1->plane, &p2->plane) ) continue; count2++; sep = Findpassages (p1->winding, p2->winding); if (!sep) { // Error ("No seperating planes found in portal pair"); count_sep++; sep = malloc(sizeof(*sep)); sep->next = NULL; sep->plane = p1->plane; } passages = malloc(sizeof(*passages)); passages->planes = sep; passages->from = p1->leaf; passages->to = p2->leaf; passages->next = l->passages; l->passages = passages; } } } printf ("numpassages: %i (%i)\n", count2, count); printf ("total passages: %i\n", count_sep); } //============================================================================= /* ============ LoadPortals ============ */ 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 ("LoadPortals: couldn't read %s\n",name); printf ("No vising performed.\n"); exit (1); } } if (fscanf (f,"%79s\n%i\n%i\n",magic, &portalleafs, &numportals) != 3) Error ("LoadPortals: failed to read header"); if (strcmp(magic,PORTALFILE)) Error ("LoadPortals: not a portal file"); 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 = malloc(2*numportals*sizeof(portal_t)); memset (portals, 0, 2*numportals*sizeof(portal_t)); leafs = 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 MAX_POINTS_ON_WINDING) Error ("LoadPortals: portal %i has too many points", i); if ( (unsigned)leafnums[0] > portalleafs || (unsigned)leafnums[1] > portalleafs) Error ("LoadPortals: reading portal %i", i); w = p->winding = NewWinding (numpoints); w->original = true; w->numpoints = numpoints; for (j=0 ; jpoints[j][k] = v[k]; } 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; VectorSubtract (vec3_origin, 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