// An adaptation of the Quake vis utility. #include #include #include "zdbsp.h" #include "nodebuild.h" #include "rejectbuilder.h" #include "templates.h" enum { SIDE_FRONT, SIDE_BACK, SIDE_ON }; /* each portal will have a list of all possible to see from first portal if (!thread->portalmightsee[portalnum]) portal mightsee for p2 = all other portals in leaf get sperating planes for all portals that might be seen by p2 mark as unseen if not present in seperating plane flood fill a new mightsee save as passagemightsee void CalcMightSee (leaf_t *leaf, */ int FRejectBuilder::CountBits (BYTE *bits, int numbits) { int i; int c; c = 0; for (i=0 ; i>3] & (1<<(i&7)) ) c++; return c; } int c_fullskip; int c_portalskip, c_leafskip; int c_vistest, c_mighttest; int c_chop, c_nochop; int active; void FRejectBuilder::CheckStack (FLeaf *leaf, FThreadData *thread) { PStack *p, *p2; for (p = thread->pstack_head.next; p != NULL; p = p->next) { // _printf ("="); if (p->leaf == leaf) throw exception("CheckStack: leaf recursion"); for (p2 = thread->pstack_head.next; p2 != p; p2 = p2->next) if (p2->leaf == p->leaf) throw exception("CheckStack: late leaf recursion"); } // _printf ("\n"); } FRejectBuilder::FWinding *FRejectBuilder::AllocStackWinding (PStack *stack) const { for (int i = 0; i < 3; i++) { if (stack->freewindings[i]) { stack->freewindings[i] = false; return &stack->windings[i]; } } throw exception("AllocStackWinding: failed"); } void FRejectBuilder::FreeStackWinding (FWinding *w, PStack *stack) const { int i; i = w - stack->windings; if (i < 0 || i > 2) return; // not from local if (stack->freewindings[i]) throw exception("FreeStackWinding: already free"); stack->freewindings[i] = true; } /* ============== VisChopWinding ============== */ FRejectBuilder::FWinding *FRejectBuilder::VisChopWinding (FWinding *in, PStack *stack, FLine *split) { int side1, side2; FPoint mid; FWinding *neww; // determine sides for each point side1 = PointOnSide (in->points[0], *split); side2 = PointOnSide (in->points[1], *split); if (side1 <= 0 && side2 <= 0) { // completely on front side return in; } if (side1 >= 0 && side2 >= 0) { // completely on back side FreeStackWinding (in, stack); return NULL; } neww = AllocStackWinding (stack); // generate a split point double v2x = (double)in->points[0].x; double v2y = (double)in->points[0].y; double v2dx = (double)in->points[1].x - v2x; double v2dy = (double)in->points[1].y - v2y; double v1dx = (double)split->dx; double v1dy = (double)split->dy; double den = v1dy*v2dx - v1dx*v2dy; if (den == 0.0) { // parallel return in; } double v1x = (double)split->x; double v1y = (double)split->y; double num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx; double frac = num / den; mid.x = in->points[0].x + fixed_t(v2dx * frac); mid.y = in->points[0].y + fixed_t(v2dy * frac); if (side1 <= 0) { neww->points[0] = in->points[0]; neww->points[1] = mid; } else { neww->points[0] = mid; neww->points[1] = in->points[1]; } // free the original winding FreeStackWinding (in, stack); return neww; } /* ============== ClipToSeperators Source, pass, and target are an ordering of portals. Generates seperating planes canidates by taking two points from source and one point from pass, and clips target by them. If target is totally clipped away, that portal can not be seen through. Normal clip keeps target on the same side as pass, which is correct if the order goes source, pass, target. If the order goes pass, source, target then flipclip should be set. ============== */ FRejectBuilder::FWinding *FRejectBuilder::ClipToSeperators (FWinding *source, FWinding *pass, FWinding *target, bool flipclip, PStack *stack) { int i, j; FLine line; int d; bool fliptest; // check all combinations for (i = 0; i < 2; i++) { // find a vertex of pass that makes a line 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 < 2; j++) { line.x = source->points[i].x; line.y = source->points[i].y; line.dx = pass->points[j].x - line.x; line.dy = pass->points[j].y - line.y; // // find out which side of the generated seperating line has the // source portal // fliptest = false; d = PointOnSide (source->points[!i], line); if (d > 0) { // source is on the back side, so we want all // pass and target on the front side fliptest = false; } else if (d < 0) { // source in on the front side, so we want all // pass and target on the back side fliptest = true; } else { // colinear with source portal continue; } // // flip the line if the source portal is backwards // if (fliptest) { line.Flip (); } // // if all of the pass portal points are now on the front side, // this is the seperating line // d = PointOnSide (pass->points[!j], line); if (d >= 0) { // == 0: colinear with seperating plane // > 0: points on back side; not a seperating plane continue; } // // flip the line if we want the back side // if (flipclip) { line.Flip (); } #ifdef SEPERATORCACHE stack->seperators[flipclip][stack->numseperators[flipclip]] = line; if (++stack->numseperators[flipclip] >= MAX_SEPERATORS) throw exception("MAX_SEPERATORS"); #endif // // clip target by the seperating plane // target = VisChopWinding (target, stack, &line); if (!target) { // target is not visible return NULL; } break; // optimization by Antony Suter } } return target; } /* ================== RecursiveLeafFlow Flood fill through the leafs If src_portal is NULL, this is the originating leaf ================== */ void FRejectBuilder::RecursiveLeafFlow (int leafnum, FThreadData *thread, PStack *prevstack) { PStack stack; VPortal *p; FLine backline; FLeaf *leaf; int i, j; long *test, *might, *prevmight, *vis, more; int pnum; thread->c_chains++; leaf = &leafs[leafnum]; // CheckStack (leaf, thread); prevstack->next = &stack; stack.next = NULL; stack.leaf = leaf; stack.portal = NULL; stack.depth = prevstack->depth + 1; #ifdef SEPERATORCACHE stack.numseperators[0] = 0; stack.numseperators[1] = 0; #endif might = (long *)stack.mightsee; vis = (long *)thread->base->portalvis; // check all portals for flowing into other leafs for (i = 0; i < leaf->numportals; i++) { p = leaf->portals[i]; if (p->removed) continue; pnum = p - portals; /* MrE: portal trace debug code { int portaltrace[] = {13, 16, 17, 37}; pstack_t *s; s = &thread->pstack_head; for (j = 0; s->next && j < sizeof(portaltrace)/sizeof(int) - 1; j++, s = s->next) { if (s->portal->num != portaltrace[j]) break; } if (j >= sizeof(portaltrace)/sizeof(int) - 1) { if (p->num == portaltrace[j]) n = 0; //traced through all the portals } } */ if ( ! (prevstack->mightsee[pnum >> 3] & (1<<(pnum&7)) ) ) { continue; // can't possibly see it } // if the portal can't see anything we haven't already seen, skip it if (p->status == STAT_Done) { test = (long *)p->portalvis; } else { test = (long *)p->portalflood; } more = 0; prevmight = (long *)prevstack->mightsee; for (j = 0; j < portallongs; j++) { might[j] = prevmight[j] & test[j]; more |= (might[j] & ~vis[j]); } if (!more && (thread->base->portalvis[pnum>>3] & (1<<(pnum&7))) ) { // can't see anything new continue; } // get line of portal and point into the neighbor leaf backline = stack.portalline = p->line; backline.Flip (); // c_portalcheck++; stack.portal = p; stack.next = NULL; stack.freewindings[0] = true; stack.freewindings[1] = true; stack.freewindings[2] = true; stack.pass = VisChopWinding (&p->winding, &stack, &thread->pstack_head.portalline); if (!stack.pass) { continue; } stack.source = VisChopWinding (prevstack->source, &stack, &backline); if (!stack.source) { continue; } if (!prevstack->pass) { // the second leaf can only be blocked if coplanar // mark the portal as visible thread->base->portalvis[pnum>>3] |= (1<<(pnum&7)); RecursiveLeafFlow (p->leaf, thread, &stack); continue; } #ifdef SEPERATORCACHE if (stack.numseperators[0]) { for (n = 0; n < stack.numseperators[0]; n++) { stack.pass = VisChopWinding (stack.pass, &stack, &stack.seperators[0][n]); if (!stack.pass) break; // target is not visible } if (n < stack.numseperators[0]) continue; } else { stack.pass = ClipToSeperators (prevstack->source, prevstack->pass, stack.pass, false, &stack); } #else stack.pass = ClipToSeperators (stack.source, prevstack->pass, stack.pass, false, &stack); #endif if (!stack.pass) continue; #ifdef SEPERATORCACHE if (stack.numseperators[1]) { for (n = 0; n < stack.numseperators[1]; n++) { stack.pass = VisChopWinding (stack.pass, &stack, &stack.seperators[1][n]); if (!stack.pass) break; // target is not visible } } else { stack.pass = ClipToSeperators (prevstack->pass, prevstack->source, stack.pass, true, &stack); } #else stack.pass = ClipToSeperators (prevstack->pass, stack.source, stack.pass, true, &stack); #endif if (!stack.pass) continue; // mark the portal as visible thread->base->portalvis[pnum>>3] |= (1<<(pnum&7)); // flow through it for real RecursiveLeafFlow (p->leaf, thread, &stack); // stack.next = NULL; } } /* =============== PortalFlow generates the portalvis bit vector =============== */ void FRejectBuilder::PortalFlow (int portalnum) { FThreadData data; int i; VPortal *p; int c_might, c_can; #ifdef MREDEBUG printf("\r%6d", portalnum); #endif p = sorted_portals[portalnum]; if (p->removed) { p->status = STAT_Done; return; } if (p->nummightsee == 0) { p->status = STAT_Done; return; } p->status = STAT_Working; c_might = p->nummightsee;//CountBits (p->portalflood, numportals); memset (&data, 0, sizeof(data)); data.base = p; data.pstack_head.portal = p; data.pstack_head.source = &p->winding; data.pstack_head.portalline = p->line; data.pstack_head.depth = 0; for (i = 0; i < portallongs; i++) { ((long *)data.pstack_head.mightsee)[i] = ((long *)p->portalflood)[i]; } RecursiveLeafFlow (p->leaf, &data, &data.pstack_head); p->status = STAT_Done; c_can = CountBits (p->portalvis, numportals); //printf ("portal:%4i mightsee:%4i cansee:%4i (%i chains)\n", // (int)(p - portals), c_might, c_can, data.c_chains); } /* ================== RecursivePassageFlow ================== */ void FRejectBuilder::RecursivePassageFlow (VPortal *portal, FThreadData *thread, PStack *prevstack) { PStack stack; VPortal *p; FLeaf *leaf; FPassage *passage, *nextpassage; int i, j; long *might, *vis, *prevmight, *cansee, *portalvis, more; int pnum; // thread->c_chains++; leaf = &leafs[portal->leaf]; // CheckStack (leaf, thread); prevstack->next = &stack; stack.next = NULL; // stack.leaf = leaf; // stack.portal = NULL; stack.depth = prevstack->depth + 1; vis = (long *)thread->base->portalvis; passage = portal->passages; nextpassage = passage; // check all portals for flowing into other leafs for (i = 0; i < leaf->numportals; i++, passage = nextpassage) { p = leaf->portals[i]; if (p->removed) continue; nextpassage = passage->next; pnum = p - portals; if ( ! (prevstack->mightsee[pnum >> 3] & (1<<(pnum&7)) ) ) continue; // can't possibly see it prevmight = (long *)prevstack->mightsee; cansee = (long *)passage->cansee; might = (long *)stack.mightsee; memcpy(might, prevmight, portalbytes); portalvis = (p->status == STAT_Done) ? (long *)p->portalvis : (long *)p->portalflood; more = 0; for (j = 0; j < portallongs; j++) { if (*might) { *might &= *cansee++ & *portalvis++; more |= (*might & ~vis[j]); } else { cansee++; portalvis++; } might++; } if (!more && (thread->base->portalvis[pnum>>3] & (1<<(pnum&7))) ) { // can't see anything new continue; } // stack.portal = p; // mark the portal as visible thread->base->portalvis[pnum>>3] |= (1<<(pnum&7)); // flow through it for real RecursivePassageFlow(p, thread, &stack); // stack.next = NULL; } } /* =============== PassageFlow =============== */ void FRejectBuilder::PassageFlow (int portalnum) { FThreadData data; int i; VPortal *p; // int c_might, c_can; #ifdef MREDEBUG printf("\r%6d", portalnum); #endif p = sorted_portals[portalnum]; if (p->removed) { p->status = STAT_Done; return; } p->status = STAT_Working; // c_might = CountBits (p->portalflood, numportals); memset (&data, 0, sizeof(data)); data.base = p; data.pstack_head.portal = p; data.pstack_head.source = &p->winding; data.pstack_head.portalline = p->line; data.pstack_head.depth = 0; for (i = 0; i < portallongs; i++) { ((long *)data.pstack_head.mightsee)[i] = ((long *)p->portalflood)[i]; } RecursivePassageFlow (p, &data, &data.pstack_head); p->status = STAT_Done; /* c_can = CountBits (p->portalvis, numportals); qprintf ("portal:%4i mightsee:%4i cansee:%4i (%i chains)\n", (int)(p - portals), c_might, c_can, data.c_chains); */ } /* ================== RecursivePassagePortalFlow ================== */ void FRejectBuilder::RecursivePassagePortalFlow (VPortal *portal, FThreadData *thread, PStack *prevstack) { PStack stack; VPortal *p; FLeaf *leaf; FLine backline; FPassage *passage, *nextpassage; int i, j; long *might, *vis, *prevmight, *cansee, *portalvis, more; int pnum; // thread->c_chains++; leaf = &leafs[portal->leaf]; // CheckStack (leaf, thread); prevstack->next = &stack; stack.next = NULL; stack.leaf = leaf; stack.portal = NULL; stack.depth = prevstack->depth + 1; #ifdef SEPERATORCACHE stack.numseperators[0] = 0; stack.numseperators[1] = 0; #endif vis = (long *)thread->base->portalvis; passage = portal->passages; nextpassage = passage; // check all portals for flowing into other leafs for (i = 0; i < leaf->numportals; i++, passage = nextpassage) { p = leaf->portals[i]; if (p->removed) continue; nextpassage = passage->next; pnum = p - portals; if ( ! (prevstack->mightsee[pnum >> 3] & (1<<(pnum&7)) ) ) continue; // can't possibly see it prevmight = (long *)prevstack->mightsee; cansee = (long *)passage->cansee; might = (long *)stack.mightsee; memcpy(might, prevmight, portalbytes); portalvis = (p->status == STAT_Done) ? (long *) p->portalvis : (long *) p->portalflood; more = 0; for (j = 0; j < portallongs; j++) { if (*might) { *might &= *cansee++ & *portalvis++; more |= (*might & ~vis[j]); } else { cansee++; portalvis++; } might++; } if (!more && (thread->base->portalvis[pnum>>3] & (1<<(pnum&7))) ) { // can't see anything new continue; } // get line of portal, point front into the neighbor leaf backline = stack.portalline = p->line; backline.Flip (); // c_portalcheck++; stack.portal = p; stack.next = NULL; stack.freewindings[0] = true; stack.freewindings[1] = true; stack.freewindings[2] = true; stack.pass = VisChopWinding (&p->winding, &stack, &thread->pstack_head.portalline); if (!stack.pass) continue; stack.source = VisChopWinding (prevstack->source, &stack, &backline); if (!stack.source) continue; if (!prevstack->pass) { // the second leaf can only be blocked if colinear // mark the portal as visible thread->base->portalvis[pnum>>3] |= (1<<(pnum&7)); RecursivePassagePortalFlow (p, thread, &stack); continue; } #ifdef SEPERATORCACHE if (stack.numseperators[0]) { for (n = 0; n < stack.numseperators[0]; n++) { stack.pass = VisChopWinding (stack.pass, &stack, &stack.seperators[0][n]); if (!stack.pass) break; // target is not visible } if (n < stack.numseperators[0]) continue; } else { stack.pass = ClipToSeperators (prevstack->source, prevstack->pass, stack.pass, false, &stack); } #else stack.pass = ClipToSeperators (stack.source, prevstack->pass, stack.pass, false, &stack); #endif if (!stack.pass) continue; #ifdef SEPERATORCACHE if (stack.numseperators[1]) { for (n = 0; n < stack.numseperators[1]; n++) { stack.pass = VisChopWinding (stack.pass, &stack, &stack.seperators[1][n]); if (!stack.pass) break; // target is not visible } } else { stack.pass = ClipToSeperators (prevstack->pass, prevstack->source, stack.pass, true, &stack); } #else stack.pass = ClipToSeperators (prevstack->pass, stack.source, stack.pass, true, &stack); #endif if (!stack.pass) continue; // mark the portal as visible thread->base->portalvis[pnum>>3] |= (1<<(pnum&7)); // flow through it for real RecursivePassagePortalFlow(p, thread, &stack); // stack.next = NULL; } } /* =============== PassagePortalFlow =============== */ void FRejectBuilder::PassagePortalFlow (int portalnum) { FThreadData data; int i; VPortal *p; // int c_might, c_can; #ifdef MREDEBUG printf("\r%6d", portalnum); #endif p = sorted_portals[portalnum]; if (p->removed) { p->status = STAT_Done; return; } p->status = STAT_Working; // c_might = CountBits (p->portalflood, numportals); memset (&data, 0, sizeof(data)); data.base = p; data.pstack_head.portal = p; data.pstack_head.source = &p->winding; data.pstack_head.portalline = p->line; data.pstack_head.depth = 0; for (i = 0; i < portallongs; i++) { ((long *)data.pstack_head.mightsee)[i] = ((long *)p->portalflood)[i]; } RecursivePassagePortalFlow (p, &data, &data.pstack_head); p->status = STAT_Done; /* c_can = CountBits (p->portalvis, numportals); qprintf ("portal:%4i mightsee:%4i cansee:%4i (%i chains)\n", (int)(p - portals), c_might, c_can, data.c_chains); */ } FRejectBuilder::FWinding *FRejectBuilder::PassageChopWinding (FWinding *in, FWinding *out, FLine *split) { int side1, side2; FPoint mid; FWinding *neww; // determine sides for each point side1 = PointOnSide (in->points[0], *split); side2 = PointOnSide (in->points[1], *split); if (side1 <= 0 && side2 <= 0) { // completely on front side return in; } if (side1 >= 0 && side2 >= 0) { // completely on back side return NULL; } neww = out; // generate a split point double v2x = (double)in->points[0].x; double v2y = (double)in->points[0].y; double v2dx = (double)in->points[1].x - v2x; double v2dy = (double)in->points[1].y - v2y; double v1dx = (double)split->dx; double v1dy = (double)split->dy; double den = v1dy*v2dx - v1dx*v2dy; if (den == 0.0) { // parallel return in; } double v1x = (double)split->x; double v1y = (double)split->y; double num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx; double frac = num / den; mid.x = in->points[0].x + fixed_t(v2dx * frac); mid.y = in->points[0].y + fixed_t(v2dy * frac); if (side1 <= 0) { neww->points[0] = in->points[0]; neww->points[1] = mid; } else { neww->points[0] = mid; neww->points[1] = in->points[1]; } return neww; } /* =============== AddSeperators =============== */ int FRejectBuilder::AddSeperators (FWinding *source, FWinding *pass, bool flipclip, FLine *seperators, int maxseperators) { int i, j; FLine line; int d; int numseperators; bool fliptest; numseperators = 0; // check all combinations for (i = 0; i < 2; i++) { // find 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 < 2; j++) { line.x = source->points[i].x; line.y = source->points[i].y; line.dx = pass->points[j].x - line.x; line.dy = pass->points[j].y - line.y; // // find out which side of the generated seperating plane has the // source portal // fliptest = false; d = PointOnSide (source->points[!i], line); if (d > 0) { // source is on the back side, so we want all // pass and target on the front side fliptest = false; } else if (d < 0) { // source in on the front side, so we want all // pass and target on the back side fliptest = true; } else { // colinear with source portal continue; } // // flip the line if the source portal is backwards // if (fliptest) { line.Flip (); } // // if all of the pass portal points are now on the positive side, // this is the seperating plane // d = PointOnSide (pass->points[!j], line); if (d >= 0) { // == 0: colinear with seperating plane // > 0: points on back side; not a seperating plane continue; } // // flip the line if we want the back side // if (flipclip) { line.Flip (); } if (numseperators >= maxseperators) throw exception("max seperators"); seperators[numseperators] = line; numseperators++; break; } } return numseperators; } /* =============== CreatePassages MrE: create passages from one portal to all the portals in the leaf the portal leads to every passage has a cansee bit string with all the portals that can be seen through the passage =============== */ void FRejectBuilder::CreatePassages (int portalnum) { int i, j, k, numseperators, numsee; VPortal *portal, *p, *target; FLeaf *leaf; FPassage *passage, *lastpassage; FLine seperators[MAX_SEPERATORS*2]; FWinding in, out, *res; #ifdef MREDEBUG printf("\r%6d", portalnum); #endif portal = sorted_portals[portalnum]; if (portal->removed) { portal->status = STAT_Done; return; } lastpassage = NULL; leaf = &leafs[portal->leaf]; for (i = 0; i < leaf->numportals; i++) { target = leaf->portals[i]; if (target->removed) continue; passage = (FPassage *) malloc(sizeof(FPassage) + portalbytes); memset(passage, 0, sizeof(FPassage) + portalbytes); numseperators = AddSeperators(&portal->winding, &target->winding, false, seperators, MAX_SEPERATORS*2); numseperators += AddSeperators(&target->winding, &portal->winding, true, &seperators[numseperators], MAX_SEPERATORS*2-numseperators); passage->next = NULL; if (lastpassage) lastpassage->next = passage; else portal->passages = passage; lastpassage = passage; numsee = 0; //create the passage->cansee for (j = 0; j < numportals; j++) { p = &portals[j]; if (p->removed) continue; if ( ! (target->portalflood[j >> 3] & (1<<(j&7)) ) ) continue; if ( ! (portal->portalflood[j >> 3] & (1<<(j&7)) ) ) continue; for (k = 0; k < numseperators; k++) { //check if completely on the back of the seperator line if (PointOnSide (p->line, seperators[k]) > 0) { FPoint pt2 = p->line; pt2.x += p->line.dx; pt2.y += p->line.dy; if (PointOnSide (pt2, seperators[k]) > 0) { break; } } } if (k < numseperators) { continue; } memcpy(&in, &p->winding, sizeof(FWinding)); for (k = 0; k < numseperators; k++) { res = PassageChopWinding(&in, &out, &seperators[k]); if (res == &out) memcpy(&in, &out, sizeof(FWinding)); if (res == NULL) break; } if (k < numseperators) continue; passage->cansee[j >> 3] |= (1<<(j&7)); numsee++; } } } void FRejectBuilder::PassageMemory () { int i, j, totalmem, totalportals; VPortal *portal, *target; FLeaf *leaf; totalmem = 0; totalportals = 0; for (i = 0; i < numportals; i++) { portal = sorted_portals[i]; if (portal->removed) continue; leaf = &leafs[portal->leaf]; for (j = 0; j < leaf->numportals; j++) { target = leaf->portals[j]; if (target->removed) continue; totalmem += sizeof(FPassage) + portalbytes; totalportals++; } } printf("\n%7i average number of passages per leaf\n", totalportals / numportals); printf("%7i MB required passage memory\n", totalmem >> 10 >> 10); } /* =============================================================================== This is a rough first-order aproximation that is used to trivially reject some of the final calculations. Calculates portalfront and portalflood bit vectors thinking about: typedef struct passage_s { struct passage_s *next; struct portal_s *to; struct sep_s *seperators; byte *mightsee; } passage_t; typedef struct portal_s { struct passage_s *passages; int leaf; // leaf portal faces into } portal_s; leaf = portal->leaf clear for all portals calc portal visibility clear bit vector for all passages passage visibility for a portal to be visible to a passage, it must be on the front of all seperating planes, and both portals must be behind the new portal =============================================================================== */ int c_flood, c_vis; /* ================== SimpleFlood ================== */ void FRejectBuilder::SimpleFlood (VPortal *srcportal, int leafnum) { int i; FLeaf *leaf; VPortal *p; int pnum; leaf = &leafs[leafnum]; for (i = 0; i < leaf->numportals; i++) { p = leaf->portals[i]; if (p->removed) continue; pnum = p - portals; if ((srcportal->portalfront[pnum>>3] & (1<<(pnum&7))) && !(srcportal->portalflood[pnum>>3] & (1<<(pnum&7))) ) { srcportal->portalflood[pnum>>3] |= (1<<(pnum&7)); SimpleFlood (srcportal, p->leaf); } } } /* ============== BasePortalVis ============== */ void FRejectBuilder::BasePortalVis (int portalnum) { int j, p1, p2; VPortal *tp, *p; p = portals+portalnum; if (p->removed) return; p->portalfront = new BYTE[portalbytes]; memset (p->portalfront, 0, portalbytes); p->portalflood = new BYTE[portalbytes]; memset (p->portalflood, 0, portalbytes); p->portalvis = new BYTE[portalbytes]; memset (p->portalvis, 0, portalbytes); for (j = 0, tp = portals; j < numportals; j++, tp++) { if (j == portalnum) continue; if (tp->removed) continue; //p->portalfront[j>>3] |= (1<<(j&7)); //continue; // The target portal must be in front of this one if ((p1 = PointOnSide (tp->winding.points[0], p->line)) > 0 || (p2 = PointOnSide (tp->winding.points[1], p->line)) > 0) { continue; } // Portals must not be colinear if ((p1 | p2) == 0) { continue; } // This portal must be behind the target portal if (PointOnSide (p->winding.points[0], tp->line) < 0 || PointOnSide (p->winding.points[1], tp->line) < 0) { continue; } p->portalfront[j>>3] |= (1<<(j&7)); } SimpleFlood (p, p->leaf); p->nummightsee = CountBits (p->portalflood, numportals); // _printf ("portal %i: %i mightsee\n", portalnum, p->nummightsee); c_flood += p->nummightsee; } /* =============================================================================== This is a second order aproximation Calculates portalvis bit vector WAAAAAAY too slow. =============================================================================== */ /* ================== RecursiveLeafBitFlow ================== */ void FRejectBuilder::RecursiveLeafBitFlow (int leafnum, BYTE *mightsee, BYTE *cansee) { VPortal *p; FLeaf *leaf; int i, j; long more; int pnum; BYTE newmight[MAX_PORTALS/8]; leaf = &leafs[leafnum]; // check all portals for flowing into other leafs for (i = 0; i < leaf->numportals; i++) { p = leaf->portals[i]; if (p->removed) continue; pnum = p - portals; // if some previous portal can't see it, skip if (! (mightsee[pnum>>3] & (1<<(pnum&7)) ) ) continue; // if this portal can see some portals we mightsee, recurse more = 0; for (j = 0; j < portallongs; j++) { ((long *)newmight)[j] = ((long *)mightsee)[j] & ((long *)p->portalflood)[j]; more |= ((long *)newmight)[j] & ~((long *)cansee)[j]; } if (!more) continue; // can't see anything new cansee[pnum>>3] |= (1<<(pnum&7)); RecursiveLeafBitFlow (p->leaf, newmight, cansee); } } /* ============== BetterPortalVis ============== */ void FRejectBuilder::BetterPortalVis (int portalnum) { VPortal *p; p = portals+portalnum; if (p->removed) return; RecursiveLeafBitFlow (p->leaf, p->portalflood, p->portalvis); // build leaf vis information p->nummightsee = CountBits (p->portalvis, numportals); c_vis += p->nummightsee; }