2 ===========================================================================
3 Copyright (C) 1997-2006 Id Software, Inc.
5 This file is part of Quake 2 Tools source code.
7 Quake 2 Tools source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
12 Quake 2 Tools source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Quake 2 Tools source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 ===========================================================================
26 each portal will have a list of all possible to see from first portal
28 if (!thread->portalmightsee[portalnum])
32 for p2 = all other portals in leaf
34 for all portals that might be seen by p2
35 mark as unseen if not present in seperating plane
36 flood fill a new mightsee
37 save as passagemightsee
40 void CalcMightSee (leaf_t *leaf,
43 int CountBits (byte *bits, int numbits)
49 for (i=0 ; i<numbits ; i++)
50 if (bits[i>>3] & (1<<(i&7)) )
57 int c_portalskip, c_leafskip;
58 int c_vistest, c_mighttest;
64 void CheckStack (leaf_t *leaf, threaddata_t *thread)
68 for (p=thread->pstack_head.next ; p ; p=p->next)
72 Error ("CheckStack: leaf recursion");
73 for (p2=thread->pstack_head.next ; p2 != p ; p2=p2->next)
74 if (p2->leaf == p->leaf)
75 Error ("CheckStack: late leaf recursion");
81 winding_t *AllocStackWinding (pstack_t *stack)
87 if (stack->freewindings[i])
89 stack->freewindings[i] = 0;
90 return &stack->windings[i];
94 Error ("AllocStackWinding: failed");
99 void FreeStackWinding (winding_t *w, pstack_t *stack)
103 i = w - stack->windings;
106 return; // not from local
108 if (stack->freewindings[i])
109 Error ("FreeStackWinding: allready free");
110 stack->freewindings[i] = 1;
119 winding_t *ChopWinding (winding_t *in, pstack_t *stack, plane_t *split)
130 counts[0] = counts[1] = counts[2] = 0;
132 // determine sides for each point
133 for (i=0 ; i<in->numpoints ; i++)
135 dot = DotProduct (in->points[i], split->normal);
138 if (dot > ON_EPSILON)
139 sides[i] = SIDE_FRONT;
140 else if (dot < -ON_EPSILON)
141 sides[i] = SIDE_BACK;
150 return in; // completely on front side
154 FreeStackWinding (in, stack);
161 neww = AllocStackWinding (stack);
165 for (i=0 ; i<in->numpoints ; i++)
169 if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING)
171 FreeStackWinding (neww, stack);
172 return in; // can't chop -- fall back to original
175 if (sides[i] == SIDE_ON)
177 VectorCopy (p1, neww->points[neww->numpoints]);
182 if (sides[i] == SIDE_FRONT)
184 VectorCopy (p1, neww->points[neww->numpoints]);
188 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
191 if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING)
193 FreeStackWinding (neww, stack);
194 return in; // can't chop -- fall back to original
197 // generate a split point
198 p2 = in->points[(i+1)%in->numpoints];
200 dot = dists[i] / (dists[i]-dists[i+1]);
201 for (j=0 ; j<3 ; j++)
202 { // avoid round off error when possible
203 if (split->normal[j] == 1)
204 mid[j] = split->dist;
205 else if (split->normal[j] == -1)
206 mid[j] = -split->dist;
208 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
211 VectorCopy (mid, neww->points[neww->numpoints]);
215 // free the original winding
216 FreeStackWinding (in, stack);
226 Source, pass, and target are an ordering of portals.
228 Generates seperating planes canidates by taking two points from source and one
229 point from pass, and clips target by them.
231 If target is totally clipped away, that portal can not be seen through.
233 Normal clip keeps target on the same side as pass, which is correct if the
234 order goes source, pass, target. If the order goes pass, source, target then
235 flipclip should be set.
238 winding_t *ClipToSeperators (winding_t *source, winding_t *pass, winding_t *target, qboolean flipclip, pstack_t *stack)
248 // check all combinations
249 for (i=0 ; i<source->numpoints ; i++)
251 l = (i+1)%source->numpoints;
252 VectorSubtract (source->points[l] , source->points[i], v1);
254 // fing a vertex of pass that makes a plane that puts all of the
255 // vertexes of pass on the front side and all of the vertexes of
256 // source on the back side
257 for (j=0 ; j<pass->numpoints ; j++)
259 VectorSubtract (pass->points[j], source->points[i], v2);
261 plane.normal[0] = v1[1]*v2[2] - v1[2]*v2[1];
262 plane.normal[1] = v1[2]*v2[0] - v1[0]*v2[2];
263 plane.normal[2] = v1[0]*v2[1] - v1[1]*v2[0];
265 // if points don't make a valid plane, skip it
267 length = plane.normal[0] * plane.normal[0]
268 + plane.normal[1] * plane.normal[1]
269 + plane.normal[2] * plane.normal[2];
271 if (length < ON_EPSILON)
274 length = 1/sqrt(length);
276 plane.normal[0] *= length;
277 plane.normal[1] *= length;
278 plane.normal[2] *= length;
280 plane.dist = DotProduct (pass->points[j], plane.normal);
283 // find out which side of the generated seperating plane has the
287 for (k=0 ; k<source->numpoints ; k++)
289 if (k == i || k == l)
291 d = DotProduct (source->points[k], plane.normal) - plane.dist;
293 { // source is on the negative side, so we want all
294 // pass and target on the positive side
298 else if (d > ON_EPSILON)
299 { // source is on the positive side, so we want all
300 // pass and target on the negative side
305 if (k == source->numpoints)
306 continue; // planar with source portal
308 // flip the normal if the source portal is backwards
312 VectorSubtract (vec3_origin, plane.normal, plane.normal);
313 plane.dist = -plane.dist;
316 // if all of the pass portal points are now on the positive side,
317 // this is the seperating plane
319 counts[0] = counts[1] = counts[2] = 0;
320 for (k=0 ; k<pass->numpoints ; k++)
324 d = DotProduct (pass->points[k], plane.normal) - plane.dist;
327 else if (d > ON_EPSILON)
332 if (k != pass->numpoints)
333 continue; // points on negative side, not a seperating plane
336 continue; // planar with seperating plane
338 // flip the normal if we want the back side
342 VectorSubtract (vec3_origin, plane.normal, plane.normal);
343 plane.dist = -plane.dist;
347 // clip target by the seperating plane
349 target = ChopWinding (target, stack, &plane);
351 return NULL; // target is not visible
364 Flood fill through the leafs
365 If src_portal is NULL, this is the originating leaf
368 void RecursiveLeafFlow (int leafnum, threaddata_t *thread, pstack_t *prevstack)
375 long *test, *might, *vis, more;
380 leaf = &leafs[leafnum];
381 // CheckStack (leaf, thread);
383 prevstack->next = &stack;
389 might = (long *)stack.mightsee;
390 vis = (long *)thread->base->portalvis;
392 // check all portals for flowing into other leafs
393 for (i=0 ; i<leaf->numportals ; i++)
395 p = leaf->portals[i];
398 if ( ! (prevstack->mightsee[pnum >> 3] & (1<<(pnum&7)) ) )
400 continue; // can't possibly see it
403 // if the portal can't see anything we haven't allready seen, skip it
404 if (p->status == stat_done)
406 test = (long *)p->portalvis;
410 test = (long *)p->portalflood;
414 for (j=0 ; j<portallongs ; j++)
416 might[j] = ((long *)prevstack->mightsee)[j] & test[j];
417 more |= (might[j] & ~vis[j]);
421 (thread->base->portalvis[pnum>>3] & (1<<(pnum&7))) )
422 { // can't see anything new
426 // get plane of portal, point normal into the neighbor leaf
427 stack.portalplane = p->plane;
428 VectorSubtract (vec3_origin, p->plane.normal, backplane.normal);
429 backplane.dist = -p->plane.dist;
433 stack.freewindings[0] = 1;
434 stack.freewindings[1] = 1;
435 stack.freewindings[2] = 1;
440 d = DotProduct (p->origin, thread->pstack_head.portalplane.normal);
441 d -= thread->pstack_head.portalplane.dist;
446 else if (d > p->radius)
448 stack.pass = p->winding;
452 stack.pass = ChopWinding (p->winding, &stack, &thread->pstack_head.portalplane);
462 d = DotProduct (thread->base->origin, p->plane.normal);
468 else if (d < -p->radius)
470 stack.source = prevstack->source;
474 stack.source = ChopWinding (prevstack->source, &stack, &backplane);
480 if (!prevstack->pass)
481 { // the second leaf can only be blocked if coplanar
483 // mark the portal as visible
484 thread->base->portalvis[pnum>>3] |= (1<<(pnum&7));
486 RecursiveLeafFlow (p->leaf, thread, &stack);
490 stack.pass = ClipToSeperators (stack.source, prevstack->pass, stack.pass, false, &stack);
494 stack.pass = ClipToSeperators (prevstack->pass, stack.source, stack.pass, true, &stack);
498 // mark the portal as visible
499 thread->base->portalvis[pnum>>3] |= (1<<(pnum&7));
501 // flow through it for real
502 RecursiveLeafFlow (p->leaf, thread, &stack);
511 generates the portalvis bit vector
514 void PortalFlow (int portalnum)
521 p = sorted_portals[portalnum];
522 p->status = stat_working;
524 c_might = CountBits (p->portalflood, numportals*2);
526 memset (&data, 0, sizeof(data));
529 data.pstack_head.portal = p;
530 data.pstack_head.source = p->winding;
531 data.pstack_head.portalplane = p->plane;
532 for (i=0 ; i<portallongs ; i++)
533 ((long *)data.pstack_head.mightsee)[i] = ((long *)p->portalflood)[i];
534 RecursiveLeafFlow (p->leaf, &data, &data.pstack_head);
536 p->status = stat_done;
538 c_can = CountBits (p->portalvis, numportals*2);
540 qprintf ("portal:%4i mightsee:%4i cansee:%4i (%i chains)\n",
541 (int)(p - portals), c_might, c_can, data.c_chains);
546 ===============================================================================
548 This is a rough first-order aproximation that is used to trivially reject some
549 of the final calculations.
552 Calculates portalfront and portalflood bit vectors
556 typedef struct passage_s
558 struct passage_s *next;
560 stryct sep_s *seperators;
564 typedef struct portal_s
566 struct passage_s *passages;
567 int leaf; // leaf portal faces into
575 calc portal visibility
581 for a portal to be visible to a passage, it must be on the front of
582 all seperating planes, and both portals must be behind the mew portal
584 ===============================================================================
596 void SimpleFlood (portal_t *srcportal, int leafnum)
603 leaf = &leafs[leafnum];
605 for (i=0 ; i<leaf->numportals ; i++)
607 p = leaf->portals[i];
609 if ( ! (srcportal->portalfront[pnum>>3] & (1<<(pnum&7)) ) )
612 if (srcportal->portalflood[pnum>>3] & (1<<(pnum&7)) )
615 srcportal->portalflood[pnum>>3] |= (1<<(pnum&7));
617 SimpleFlood (srcportal, p->leaf);
626 void BasePortalVis (int portalnum)
633 p = portals+portalnum;
635 p->portalfront = malloc (portalbytes);
636 memset (p->portalfront, 0, portalbytes);
638 p->portalflood = malloc (portalbytes);
639 memset (p->portalflood, 0, portalbytes);
641 p->portalvis = malloc (portalbytes);
642 memset (p->portalvis, 0, portalbytes);
644 for (j=0, tp = portals ; j<numportals*2 ; j++, tp++)
649 for (k=0 ; k<w->numpoints ; k++)
651 d = DotProduct (w->points[k], p->plane.normal)
656 if (k == w->numpoints)
657 continue; // no points on front
660 for (k=0 ; k<w->numpoints ; k++)
662 d = DotProduct (w->points[k], tp->plane.normal)
667 if (k == w->numpoints)
668 continue; // no points on front
670 p->portalfront[j>>3] |= (1<<(j&7));
673 SimpleFlood (p, p->leaf);
675 p->nummightsee = CountBits (p->portalflood, numportals*2);
676 // printf ("portal %i: %i mightsee\n", portalnum, p->nummightsee);
677 c_flood += p->nummightsee;
685 ===============================================================================
687 This is a second order aproximation
689 Calculates portalvis bit vector
693 ===============================================================================
702 void RecursiveLeafBitFlow (int leafnum, byte *mightsee, byte *cansee)
709 byte newmight[MAX_PORTALS/8];
711 leaf = &leafs[leafnum];
713 // check all portals for flowing into other leafs
714 for (i=0 ; i<leaf->numportals ; i++)
716 p = leaf->portals[i];
719 // if some previous portal can't see it, skip
720 if (! (mightsee[pnum>>3] & (1<<(pnum&7)) ) )
723 // if this portal can see some portals we mightsee, recurse
725 for (j=0 ; j<portallongs ; j++)
727 ((long *)newmight)[j] = ((long *)mightsee)[j]
728 & ((long *)p->portalflood)[j];
729 more |= ((long *)newmight)[j] & ~((long *)cansee)[j];
733 continue; // can't see anything new
735 cansee[pnum>>3] |= (1<<(pnum&7));
737 RecursiveLeafBitFlow (p->leaf, newmight, cansee);
746 void BetterPortalVis (int portalnum)
750 p = portals+portalnum;
752 RecursiveLeafBitFlow (p->leaf, p->portalflood, p->portalvis);
754 // build leaf vis information
755 p->nummightsee = CountBits (p->portalvis, numportals*2);
756 c_vis += p->nummightsee;