2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 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 GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 each portal will have a list of all possible to see from first portal
27 if (!thread->portalmightsee[portalnum])
31 for p2 = all other portals in leaf
33 for all portals that might be seen by p2
34 mark as unseen if not present in seperating plane
35 flood fill a new mightsee
36 save as passagemightsee
39 void CalcMightSee (leaf_t *leaf,
42 int CountBits( byte *bits, int numbits ){
47 for ( i = 0 ; i < numbits ; i++ )
48 if ( bits[i >> 3] & ( 1 << ( i & 7 ) ) ) {
56 int c_portalskip, c_leafskip;
57 int c_vistest, c_mighttest;
63 void CheckStack( leaf_t *leaf, threaddata_t *thread ){
66 for ( p = thread->pstack_head.next ; p ; p = p->next )
69 if ( p->leaf == leaf ) {
70 Error( "CheckStack: leaf recursion" );
72 for ( p2 = thread->pstack_head.next ; p2 != p ; p2 = p2->next )
73 if ( p2->leaf == p->leaf ) {
74 Error( "CheckStack: late leaf recursion" );
81 winding_t *AllocStackWinding( pstack_t *stack ){
84 for ( i = 0 ; i < 3 ; i++ )
86 if ( stack->freewindings[i] ) {
87 stack->freewindings[i] = 0;
88 return &stack->windings[i];
92 Error( "AllocStackWinding: failed" );
97 void FreeStackWinding( winding_t *w, pstack_t *stack ){
100 i = w - stack->windings;
102 if ( i < 0 || i > 2 ) {
103 return; // not from local
106 if ( stack->freewindings[i] ) {
107 Error( "FreeStackWinding: allready free" );
109 stack->freewindings[i] = 1;
118 winding_t *Vis_ChopWinding( winding_t *in, pstack_t *stack, plane_t *split ){
128 counts[0] = counts[1] = counts[2] = 0;
130 // determine sides for each point
131 for ( i = 0 ; i < in->numpoints ; i++ )
133 dot = DotProduct( in->points[i], split->normal );
136 if ( dot > ON_EPSILON ) {
137 sides[i] = SIDE_FRONT;
139 else if ( dot < -ON_EPSILON ) {
140 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 ) {
170 FreeStackWinding( neww, stack );
171 return in; // can't chop -- fall back to original
174 if ( sides[i] == SIDE_ON ) {
175 VectorCopy( p1, neww->points[neww->numpoints] );
180 if ( sides[i] == SIDE_FRONT ) {
181 VectorCopy( p1, neww->points[neww->numpoints] );
185 if ( sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i] ) {
189 if ( neww->numpoints == MAX_POINTS_ON_FIXED_WINDING ) {
190 FreeStackWinding( neww, stack );
191 return in; // can't chop -- fall back to original
194 // generate a split point
195 p2 = in->points[( i + 1 ) % in->numpoints];
197 dot = dists[i] / ( dists[i] - dists[i + 1] );
198 for ( j = 0 ; j < 3 ; j++ )
199 { // avoid round off error when possible
200 if ( split->normal[j] == 1 ) {
201 mid[j] = split->dist;
203 else if ( split->normal[j] == -1 ) {
204 mid[j] = -split->dist;
207 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 ){
247 // check all combinations
248 for ( i = 0 ; i < source->numpoints ; i++ )
250 l = ( i + 1 ) % source->numpoints;
251 VectorSubtract( source->points[l], source->points[i], v1 );
253 // fing a vertex of pass that makes a plane that puts all of the
254 // vertexes of pass on the front side and all of the vertexes of
255 // source on the back side
256 for ( j = 0 ; j < pass->numpoints ; j++ )
258 VectorSubtract( pass->points[j], source->points[i], v2 );
260 plane.normal[0] = v1[1] * v2[2] - v1[2] * v2[1];
261 plane.normal[1] = v1[2] * v2[0] - v1[0] * v2[2];
262 plane.normal[2] = v1[0] * v2[1] - v1[1] * v2[0];
264 // if points don't make a valid plane, skip it
266 length = plane.normal[0] * plane.normal[0]
267 + plane.normal[1] * plane.normal[1]
268 + plane.normal[2] * plane.normal[2];
270 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
288 for ( k = 0 ; k < source->numpoints ; k++ )
290 if ( k == i || k == l ) {
293 d = DotProduct( source->points[k], plane.normal ) - plane.dist;
294 if ( d < -ON_EPSILON ) { // source is on the negative side, so we want all
295 // pass and target on the positive side
299 else if ( d > ON_EPSILON ) { // 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
312 // flip the normal if the source portal is backwards
315 VectorSubtract( vec3_origin, plane.normal, plane.normal );
316 plane.dist = -plane.dist;
320 // if all of the pass portal points are now on the positive side,
321 // this is the seperating plane
323 counts[0] = counts[1] = counts[2] = 0;
324 for ( k = 0 ; k < pass->numpoints ; k++ )
329 d = DotProduct( pass->points[k], plane.normal ) - plane.dist;
330 if ( d < -ON_EPSILON ) {
333 else if ( d > ON_EPSILON ) {
340 if ( k != pass->numpoints ) {
341 continue; // points on negative side, not a seperating plane
345 continue; // planar with seperating plane
348 k = ( j + 1 ) % pass->numpoints;
349 d = DotProduct( pass->points[k], plane.normal ) - plane.dist;
350 if ( d < -ON_EPSILON ) {
353 k = ( j + pass->numpoints - 1 ) % pass->numpoints;
354 d = DotProduct( pass->points[k], plane.normal ) - plane.dist;
355 if ( d < -ON_EPSILON ) {
360 // flip the normal if we want the back side
363 VectorSubtract( vec3_origin, plane.normal, plane.normal );
364 plane.dist = -plane.dist;
368 // clip target by the seperating plane
370 target = Vis_ChopWinding( target, stack, &plane );
372 return NULL; // target is not visible
386 Flood fill through the leafs
387 If src_portal is NULL, this is the originating leaf
390 void RecursiveLeafFlow( int leafnum, threaddata_t *thread, pstack_t *prevstack ){
396 long *test, *might, *vis, more;
401 leaf = &leafs[leafnum];
402 // CheckStack (leaf, thread);
404 prevstack->next = &stack;
410 might = (long *)stack.mightsee;
411 vis = (long *)thread->base->portalvis;
413 // check all portals for flowing into other leafs
414 for ( i = 0 ; i < leaf->numportals ; i++ )
416 p = leaf->portals[i];
419 if ( !( prevstack->mightsee[pnum >> 3] & ( 1 << ( pnum & 7 ) ) ) ) {
420 continue; // can't possibly see it
423 // if the portal can't see anything we haven't allready seen, skip it
424 if ( p->status == stat_done ) {
425 test = (long *)p->portalvis;
429 test = (long *)p->portalflood;
433 for ( j = 0 ; j < portallongs ; j++ )
435 might[j] = ( (long *)prevstack->mightsee )[j] & test[j];
436 more |= ( might[j] & ~vis[j] );
440 ( thread->base->portalvis[pnum >> 3] & ( 1 << ( pnum & 7 ) ) ) ) { // can't see anything new
444 // get plane of portal, point normal into the neighbor leaf
445 stack.portalplane = p->plane;
446 VectorSubtract( vec3_origin, p->plane.normal, backplane.normal );
447 backplane.dist = -p->plane.dist;
453 stack.freewindings[0] = 1;
454 stack.freewindings[1] = 1;
455 stack.freewindings[2] = 1;
461 d = DotProduct( p->origin, thread->pstack_head.portalplane.normal );
462 d -= thread->pstack_head.portalplane.dist;
463 if ( d < -p->radius ) {
466 else if ( d > p->radius ) {
467 stack.pass = p->winding;
471 stack.pass = Vis_ChopWinding( p->winding, &stack, &thread->pstack_head.portalplane );
478 stack.pass = Vis_ChopWinding( p->winding, &stack, &thread->pstack_head.portalplane );
489 d = DotProduct( thread->base->origin, p->plane.normal );
491 if ( d > p->radius ) {
494 else if ( d < -p->radius ) {
495 stack.source = prevstack->source;
499 stack.source = Vis_ChopWinding( prevstack->source, &stack, &backplane );
500 if ( !stack.source ) {
506 stack.source = Vis_ChopWinding( prevstack->source, &stack, &backplane );
507 if ( !stack.source ) {
512 if ( !prevstack->pass ) { // the second leaf can only be blocked if coplanar
514 // mark the portal as visible
515 thread->base->portalvis[pnum >> 3] |= ( 1 << ( pnum & 7 ) );
517 RecursiveLeafFlow( p->leaf, thread, &stack );
521 stack.pass = ClipToSeperators( stack.source, prevstack->pass, stack.pass, false, &stack );
526 stack.pass = ClipToSeperators( prevstack->pass, stack.source, stack.pass, true, &stack );
531 // mark the portal as visible
532 thread->base->portalvis[pnum >> 3] |= ( 1 << ( pnum & 7 ) );
534 // flow through it for real
535 RecursiveLeafFlow( p->leaf, thread, &stack );
544 generates the portalvis bit vector
547 void PortalFlow( int portalnum ){
553 p = sorted_portals[portalnum];
554 p->status = stat_working;
556 c_might = CountBits( p->portalflood, numportals * 2 );
558 memset( &data, 0, sizeof( data ) );
561 data.pstack_head.portal = p;
562 data.pstack_head.source = p->winding;
563 data.pstack_head.portalplane = p->plane;
564 for ( i = 0 ; i < portallongs ; i++ )
565 ( (long *)data.pstack_head.mightsee )[i] = ( (long *)p->portalflood )[i];
566 RecursiveLeafFlow( p->leaf, &data, &data.pstack_head );
568 p->status = stat_done;
570 c_can = CountBits( p->portalvis, numportals * 2 );
572 Sys_FPrintf( SYS_VRB, "portal:%4i mightsee:%4i cansee:%4i (%i chains)\n",
573 (int)( p - portals ), c_might, c_can, data.c_chains );
578 ===============================================================================
580 This is a rough first-order aproximation that is used to trivially reject some
581 of the final calculations.
584 Calculates portalfront and portalflood bit vectors
588 typedef struct passage_s
590 struct passage_s *next;
592 stryct sep_s *seperators;
596 typedef struct portal_s
598 struct passage_s *passages;
599 int leaf; // leaf portal faces into
607 calc portal visibility
613 for a portal to be visible to a passage, it must be on the front of
614 all seperating planes, and both portals must be behind the mew portal
616 ===============================================================================
628 void SimpleFlood( portal_t *srcportal, int leafnum ){
634 leaf = &leafs[leafnum];
636 for ( i = 0 ; i < leaf->numportals ; i++ )
638 p = leaf->portals[i];
640 if ( !( srcportal->portalfront[pnum >> 3] & ( 1 << ( pnum & 7 ) ) ) ) {
644 if ( srcportal->portalflood[pnum >> 3] & ( 1 << ( pnum & 7 ) ) ) {
648 srcportal->portalflood[pnum >> 3] |= ( 1 << ( pnum & 7 ) );
650 SimpleFlood( srcportal, p->leaf );
659 void BasePortalVis( int portalnum ){
665 p = portals + portalnum;
667 p->portalfront = malloc( portalbytes );
668 memset( p->portalfront, 0, portalbytes );
670 p->portalflood = malloc( portalbytes );
671 memset( p->portalflood, 0, portalbytes );
673 p->portalvis = malloc( portalbytes );
674 memset( p->portalvis, 0, portalbytes );
676 for ( j = 0, tp = portals ; j < numportals * 2 ; j++, tp++ )
678 if ( j == portalnum ) {
682 for ( k = 0 ; k < w->numpoints ; k++ )
684 d = DotProduct( w->points[k], p->plane.normal )
686 if ( d > ON_EPSILON ) {
690 if ( k == w->numpoints ) {
691 continue; // no points on front
695 for ( k = 0 ; k < w->numpoints ; k++ )
697 d = DotProduct( w->points[k], tp->plane.normal )
699 if ( d < -ON_EPSILON ) {
703 if ( k == w->numpoints ) {
704 continue; // no points on front
707 p->portalfront[j >> 3] |= ( 1 << ( j & 7 ) );
710 SimpleFlood( p, p->leaf );
712 p->nummightsee = CountBits( p->portalflood, numportals * 2 );
713 // printf ("portal %i: %i mightsee\n", portalnum, p->nummightsee);
714 c_flood += p->nummightsee;
722 ===============================================================================
724 This is a second order aproximation
726 Calculates portalvis bit vector
730 ===============================================================================
739 void RecursiveLeafBitFlow( int leafnum, byte *mightsee, byte *cansee ){
745 byte newmight[MAX_PORTALS / 8];
747 leaf = &leafs[leafnum];
749 // check all portals for flowing into other leafs
750 for ( i = 0 ; i < leaf->numportals ; i++ )
752 p = leaf->portals[i];
755 // if some previous portal can't see it, skip
756 if ( !( mightsee[pnum >> 3] & ( 1 << ( pnum & 7 ) ) ) ) {
760 // if this portal can see some portals we mightsee, recurse
762 for ( j = 0 ; j < portallongs ; j++ )
764 ( (long *)newmight )[j] = ( (long *)mightsee )[j]
765 & ( (long *)p->portalflood )[j];
766 more |= ( (long *)newmight )[j] & ~( (long *)cansee )[j];
770 continue; // can't see anything new
773 cansee[pnum >> 3] |= ( 1 << ( pnum & 7 ) );
775 RecursiveLeafBitFlow( p->leaf, newmight, cansee );
784 void BetterPortalVis( int portalnum ){
787 p = portals + portalnum;
789 RecursiveLeafBitFlow( p->leaf, p->portalflood, p->portalvis );
791 // build leaf vis information
792 p->nummightsee = CountBits( p->portalvis, numportals * 2 );
793 c_vis += p->nummightsee;