1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
44 each portal will have a list of all possible to see from first portal
46 if (!thread->portalmightsee[portalnum])
50 for p2 = all other portals in leaf
52 for all portals that might be seen by p2
53 mark as unseen if not present in seperating plane
54 flood fill a new mightsee
55 save as passagemightsee
58 void CalcMightSee (leaf_t *leaf,
61 int CountBits( byte *bits, int numbits ){
66 for ( i = 0 ; i < numbits ; i++ )
67 if ( bits[i >> 3] & ( 1 << ( i & 7 ) ) ) {
75 int c_portalskip, c_leafskip;
76 int c_vistest, c_mighttest;
82 void CheckStack( leaf_t *leaf, threaddata_t *thread ){
85 for ( p = thread->pstack_head.next ; p ; p = p->next )
88 if ( p->leaf == leaf ) {
89 Error( "CheckStack: leaf recursion" );
91 for ( p2 = thread->pstack_head.next ; p2 != p ; p2 = p2->next )
92 if ( p2->leaf == p->leaf ) {
93 Error( "CheckStack: late leaf recursion" );
100 fixedWinding_t *AllocStackWinding( pstack_t *stack ){
103 for ( i = 0 ; i < 3 ; i++ )
105 if ( stack->freewindings[i] ) {
106 stack->freewindings[i] = 0;
107 return &stack->windings[i];
111 Error( "AllocStackWinding: failed" );
116 void FreeStackWinding( fixedWinding_t *w, pstack_t *stack ){
119 i = w - stack->windings;
121 if ( i < 0 || i > 2 ) {
122 return; // not from local
125 if ( stack->freewindings[i] ) {
126 Error( "FreeStackWinding: allready free" );
128 stack->freewindings[i] = 1;
137 fixedWinding_t *VisChopWinding( fixedWinding_t *in, pstack_t *stack, visPlane_t *split ){
145 fixedWinding_t *neww;
147 counts[0] = counts[1] = counts[2] = 0;
149 // determine sides for each point
150 for ( i = 0 ; i < in->numpoints ; i++ )
152 dot = DotProduct( in->points[i], split->normal );
155 if ( dot > ON_EPSILON ) {
156 sides[i] = SIDE_FRONT;
158 else if ( dot < -ON_EPSILON ) {
159 sides[i] = SIDE_BACK;
169 return in; // completely on front side
173 FreeStackWinding( in, stack );
180 neww = AllocStackWinding( stack );
184 for ( i = 0 ; i < in->numpoints ; i++ )
188 if ( neww->numpoints == MAX_POINTS_ON_FIXED_WINDING ) {
189 FreeStackWinding( neww, stack );
190 return in; // can't chop -- fall back to original
193 if ( sides[i] == SIDE_ON ) {
194 VectorCopy( p1, neww->points[neww->numpoints] );
199 if ( sides[i] == SIDE_FRONT ) {
200 VectorCopy( p1, neww->points[neww->numpoints] );
204 if ( sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i] ) {
208 if ( neww->numpoints == MAX_POINTS_ON_FIXED_WINDING ) {
209 FreeStackWinding( neww, stack );
210 return in; // can't chop -- fall back to original
213 // generate a split point
214 p2 = in->points[( i + 1 ) % in->numpoints];
216 dot = dists[i] / ( dists[i] - dists[i + 1] );
217 for ( j = 0 ; j < 3 ; j++ )
218 { // avoid round off error when possible
219 if ( split->normal[j] == 1 ) {
220 mid[j] = split->dist;
222 else if ( split->normal[j] == -1 ) {
223 mid[j] = -split->dist;
226 mid[j] = p1[j] + dot * ( p2[j] - p1[j] );
230 VectorCopy( mid, neww->points[neww->numpoints] );
234 // free the original winding
235 FreeStackWinding( in, stack );
244 Source, pass, and target are an ordering of portals.
246 Generates seperating planes canidates by taking two points from source and one
247 point from pass, and clips target by them.
249 If target is totally clipped away, that portal can not be seen through.
251 Normal clip keeps target on the same side as pass, which is correct if the
252 order goes source, pass, target. If the order goes pass, source, target then
253 flipclip should be set.
256 fixedWinding_t *ClipToSeperators( fixedWinding_t *source, fixedWinding_t *pass, fixedWinding_t *target, qboolean flipclip, pstack_t *stack ){
265 // check all combinations
266 for ( i = 0 ; i < source->numpoints ; i++ )
268 l = ( i + 1 ) % source->numpoints;
269 VectorSubtract( source->points[l], source->points[i], v1 );
271 // find a vertex of pass that makes a plane that puts all of the
272 // vertexes of pass on the front side and all of the vertexes of
273 // source on the back side
274 for ( j = 0 ; j < pass->numpoints ; j++ )
276 VectorSubtract( pass->points[j], source->points[i], v2 );
278 plane.normal[0] = v1[1] * v2[2] - v1[2] * v2[1];
279 plane.normal[1] = v1[2] * v2[0] - v1[0] * v2[2];
280 plane.normal[2] = v1[0] * v2[1] - v1[1] * v2[0];
282 // if points don't make a valid plane, skip it
284 length = plane.normal[0] * plane.normal[0]
285 + plane.normal[1] * plane.normal[1]
286 + plane.normal[2] * plane.normal[2];
288 if ( length < ON_EPSILON ) {
292 length = 1 / sqrt( length );
294 plane.normal[0] *= length;
295 plane.normal[1] *= length;
296 plane.normal[2] *= length;
298 plane.dist = DotProduct( pass->points[j], plane.normal );
301 // find out which side of the generated seperating plane has the
306 for ( k = 0 ; k < source->numpoints ; k++ )
308 if ( k == i || k == l ) {
311 d = DotProduct( source->points[k], plane.normal ) - plane.dist;
312 if ( d < -ON_EPSILON ) { // source is on the negative side, so we want all
313 // pass and target on the positive side
317 else if ( d > ON_EPSILON ) { // source is on the positive side, so we want all
318 // pass and target on the negative side
323 if ( k == source->numpoints ) {
324 continue; // planar with source portal
330 // flip the normal if the source portal is backwards
333 VectorSubtract( vec3_origin, plane.normal, plane.normal );
334 plane.dist = -plane.dist;
338 // if all of the pass portal points are now on the positive side,
339 // this is the seperating plane
341 counts[0] = counts[1] = counts[2] = 0;
342 for ( k = 0 ; k < pass->numpoints ; k++ )
347 d = DotProduct( pass->points[k], plane.normal ) - plane.dist;
348 if ( d < -ON_EPSILON ) {
351 else if ( d > ON_EPSILON ) {
358 if ( k != pass->numpoints ) {
359 continue; // points on negative side, not a seperating plane
363 continue; // planar with seperating plane
366 k = ( j + 1 ) % pass->numpoints;
367 d = DotProduct( pass->points[k], plane.normal ) - plane.dist;
368 if ( d < -ON_EPSILON ) {
371 k = ( j + pass->numpoints - 1 ) % pass->numpoints;
372 d = DotProduct( pass->points[k], plane.normal ) - plane.dist;
373 if ( d < -ON_EPSILON ) {
378 // flip the normal if we want the back side
381 VectorSubtract( vec3_origin, plane.normal, plane.normal );
382 plane.dist = -plane.dist;
385 #ifdef SEPERATORCACHE
386 stack->seperators[flipclip][stack->numseperators[flipclip]] = plane;
387 if ( ++stack->numseperators[flipclip] >= MAX_SEPERATORS ) {
388 Error( "MAX_SEPERATORS" );
391 //MrE: fast check first
392 d = DotProduct( stack->portal->origin, plane.normal ) - plane.dist;
393 //if completely at the back of the seperator plane
394 if ( d < -stack->portal->radius ) {
397 //if completely on the front of the seperator plane
398 if ( d > stack->portal->radius ) {
403 // clip target by the seperating plane
405 target = VisChopWinding( target, stack, &plane );
407 return NULL; // target is not visible
410 break; // optimization by Antony Suter
421 Flood fill through the leafs
422 If src_portal is NULL, this is the originating leaf
425 void RecursiveLeafFlow( int leafnum, threaddata_t *thread, pstack_t *prevstack ){
428 visPlane_t backplane;
431 long *test, *might, *prevmight, *vis, more;
436 leaf = &leafs[leafnum];
437 // CheckStack (leaf, thread);
439 prevstack->next = &stack;
444 stack.depth = prevstack->depth + 1;
446 #ifdef SEPERATORCACHE
447 stack.numseperators[0] = 0;
448 stack.numseperators[1] = 0;
451 might = (long *)stack.mightsee;
452 vis = (long *)thread->base->portalvis;
454 // check all portals for flowing into other leafs
455 for ( i = 0; i < leaf->numportals; i++ )
457 p = leaf->portals[i];
463 /* MrE: portal trace debug code
465 int portaltrace[] = {13, 16, 17, 37};
468 s = &thread->pstack_head;
469 for (j = 0; s->next && j < sizeof(portaltrace)/sizeof(int) - 1; j++, s = s->next)
471 if (s->portal->num != portaltrace[j])
474 if (j >= sizeof(portaltrace)/sizeof(int) - 1)
476 if (p->num == portaltrace[j])
477 n = 0; //traced through all the portals
482 if ( !( prevstack->mightsee[pnum >> 3] & ( 1 << ( pnum & 7 ) ) ) ) {
483 continue; // can't possibly see it
486 // if the portal can't see anything we haven't allready seen, skip it
487 if ( p->status == stat_done ) {
488 test = (long *)p->portalvis;
492 test = (long *)p->portalflood;
496 prevmight = (long *)prevstack->mightsee;
497 for ( j = 0 ; j < portallongs ; j++ )
499 might[j] = prevmight[j] & test[j];
500 more |= ( might[j] & ~vis[j] );
504 ( thread->base->portalvis[pnum >> 3] & ( 1 << ( pnum & 7 ) ) ) ) { // can't see anything new
508 // get plane of portal, point normal into the neighbor leaf
509 stack.portalplane = p->plane;
510 VectorSubtract( vec3_origin, p->plane.normal, backplane.normal );
511 backplane.dist = -p->plane.dist;
517 stack.freewindings[0] = 1;
518 stack.freewindings[1] = 1;
519 stack.freewindings[2] = 1;
525 d = DotProduct( p->origin, thread->pstack_head.portalplane.normal );
526 d -= thread->pstack_head.portalplane.dist;
527 if ( d < -p->radius ) {
530 else if ( d > p->radius ) {
531 stack.pass = p->winding;
535 stack.pass = VisChopWinding( p->winding, &stack, &thread->pstack_head.portalplane );
542 stack.pass = VisChopWinding( p->winding, &stack, &thread->pstack_head.portalplane );
553 d = DotProduct( thread->base->origin, p->plane.normal );
557 if ( d > thread->base->radius ) {
561 //if (d < -p->radius)
562 else if ( d < -thread->base->radius ) {
563 stack.source = prevstack->source;
567 stack.source = VisChopWinding( prevstack->source, &stack, &backplane );
568 //FIXME: shouldn't we create a new source origin and radius for fast checks?
569 if ( !stack.source ) {
575 stack.source = VisChopWinding( prevstack->source, &stack, &backplane );
576 if ( !stack.source ) {
581 if ( !prevstack->pass ) { // the second leaf can only be blocked if coplanar
583 // mark the portal as visible
584 thread->base->portalvis[pnum >> 3] |= ( 1 << ( pnum & 7 ) );
586 RecursiveLeafFlow( p->leaf, thread, &stack );
590 #ifdef SEPERATORCACHE
591 if ( stack.numseperators[0] ) {
592 for ( n = 0; n < stack.numseperators[0]; n++ )
594 stack.pass = VisChopWinding( stack.pass, &stack, &stack.seperators[0][n] );
596 break; // target is not visible
599 if ( n < stack.numseperators[0] ) {
605 stack.pass = ClipToSeperators( prevstack->source, prevstack->pass, stack.pass, qfalse, &stack );
608 stack.pass = ClipToSeperators( stack.source, prevstack->pass, stack.pass, qfalse, &stack );
614 #ifdef SEPERATORCACHE
615 if ( stack.numseperators[1] ) {
616 for ( n = 0; n < stack.numseperators[1]; n++ )
618 stack.pass = VisChopWinding( stack.pass, &stack, &stack.seperators[1][n] );
620 break; // target is not visible
626 stack.pass = ClipToSeperators( prevstack->pass, prevstack->source, stack.pass, qtrue, &stack );
629 stack.pass = ClipToSeperators( prevstack->pass, stack.source, stack.pass, qtrue, &stack );
635 // mark the portal as visible
636 thread->base->portalvis[pnum >> 3] |= ( 1 << ( pnum & 7 ) );
638 // flow through it for real
639 RecursiveLeafFlow( p->leaf, thread, &stack );
649 generates the portalvis bit vector
652 void PortalFlow( int portalnum ){
658 p = sorted_portals[portalnum];
661 p->status = stat_done;
665 p->status = stat_working;
667 c_might = CountBits( p->portalflood, numportals * 2 );
669 memset( &data, 0, sizeof( data ) );
672 data.pstack_head.portal = p;
673 data.pstack_head.source = p->winding;
674 data.pstack_head.portalplane = p->plane;
675 data.pstack_head.depth = 0;
676 for ( i = 0 ; i < portallongs ; i++ )
677 ( (long *)data.pstack_head.mightsee )[i] = ( (long *)p->portalflood )[i];
679 RecursiveLeafFlow( p->leaf, &data, &data.pstack_head );
681 p->status = stat_done;
683 c_can = CountBits( p->portalvis, numportals * 2 );
685 Sys_FPrintf( SYS_VRB,"portal:%4i mightsee:%4i cansee:%4i (%i chains)\n",
686 (int)( p - portals ), c_might, c_can, data.c_chains );
694 void RecursivePassageFlow( vportal_t *portal, threaddata_t *thread, pstack_t *prevstack ){
698 passage_t *passage, *nextpassage;
700 long *might, *vis, *prevmight, *cansee, *portalvis, more;
703 leaf = &leafs[portal->leaf];
705 prevstack->next = &stack;
708 stack.depth = prevstack->depth + 1;
710 vis = (long *)thread->base->portalvis;
712 passage = portal->passages;
713 nextpassage = passage;
714 // check all portals for flowing into other leafs
715 for ( i = 0; i < leaf->numportals; i++, passage = nextpassage )
717 p = leaf->portals[i];
721 nextpassage = passage->next;
724 if ( !( prevstack->mightsee[pnum >> 3] & ( 1 << ( pnum & 7 ) ) ) ) {
725 continue; // can't possibly see it
728 // mark the portal as visible
729 thread->base->portalvis[pnum >> 3] |= ( 1 << ( pnum & 7 ) );
731 prevmight = (long *)prevstack->mightsee;
732 cansee = (long *)passage->cansee;
733 might = (long *)stack.mightsee;
734 memcpy( might, prevmight, portalbytes );
735 if ( p->status == stat_done ) {
736 portalvis = (long *) p->portalvis;
739 portalvis = (long *) p->portalflood;
742 for ( j = 0; j < portallongs; j++ )
745 *might &= *cansee++ & *portalvis++;
746 more |= ( *might & ~vis[j] );
757 // can't see anything new
761 // flow through it for real
762 RecursivePassageFlow( p, thread, &stack );
773 void PassageFlow( int portalnum ){
778 p = sorted_portals[portalnum];
781 p->status = stat_done;
785 p->status = stat_working;
787 memset( &data, 0, sizeof( data ) );
790 data.pstack_head.portal = p;
791 data.pstack_head.source = p->winding;
792 data.pstack_head.portalplane = p->plane;
793 data.pstack_head.depth = 0;
794 for ( i = 0 ; i < portallongs ; i++ )
795 ( (long *)data.pstack_head.mightsee )[i] = ( (long *)p->portalflood )[i];
797 RecursivePassageFlow( p, &data, &data.pstack_head );
799 p->status = stat_done;
804 RecursivePassagePortalFlow
807 void RecursivePassagePortalFlow( vportal_t *portal, threaddata_t *thread, pstack_t *prevstack ){
811 visPlane_t backplane;
812 passage_t *passage, *nextpassage;
814 long *might, *vis, *prevmight, *cansee, *portalvis, more;
817 // thread->c_chains++;
819 leaf = &leafs[portal->leaf];
820 // CheckStack (leaf, thread);
822 prevstack->next = &stack;
827 stack.depth = prevstack->depth + 1;
829 #ifdef SEPERATORCACHE
830 stack.numseperators[0] = 0;
831 stack.numseperators[1] = 0;
834 vis = (long *)thread->base->portalvis;
836 passage = portal->passages;
837 nextpassage = passage;
838 // check all portals for flowing into other leafs
839 for ( i = 0; i < leaf->numportals; i++, passage = nextpassage )
841 p = leaf->portals[i];
845 nextpassage = passage->next;
848 if ( !( prevstack->mightsee[pnum >> 3] & ( 1 << ( pnum & 7 ) ) ) ) {
849 continue; // can't possibly see it
852 prevmight = (long *)prevstack->mightsee;
853 cansee = (long *)passage->cansee;
854 might = (long *)stack.mightsee;
855 memcpy( might, prevmight, portalbytes );
856 if ( p->status == stat_done ) {
857 portalvis = (long *) p->portalvis;
860 portalvis = (long *) p->portalflood;
863 for ( j = 0; j < portallongs; j++ )
866 *might &= *cansee++ & *portalvis++;
867 more |= ( *might & ~vis[j] );
877 if ( !more && ( thread->base->portalvis[pnum >> 3] & ( 1 << ( pnum & 7 ) ) ) ) { // can't see anything new
881 // get plane of portal, point normal into the neighbor leaf
882 stack.portalplane = p->plane;
883 VectorSubtract( vec3_origin, p->plane.normal, backplane.normal );
884 backplane.dist = -p->plane.dist;
890 stack.freewindings[0] = 1;
891 stack.freewindings[1] = 1;
892 stack.freewindings[2] = 1;
898 d = DotProduct( p->origin, thread->pstack_head.portalplane.normal );
899 d -= thread->pstack_head.portalplane.dist;
900 if ( d < -p->radius ) {
903 else if ( d > p->radius ) {
904 stack.pass = p->winding;
908 stack.pass = VisChopWinding( p->winding, &stack, &thread->pstack_head.portalplane );
915 stack.pass = VisChopWinding( p->winding, &stack, &thread->pstack_head.portalplane );
926 d = DotProduct( thread->base->origin, p->plane.normal );
930 if ( d > thread->base->radius ) {
934 //if (d < -p->radius)
935 else if ( d < -thread->base->radius ) {
936 stack.source = prevstack->source;
940 stack.source = VisChopWinding( prevstack->source, &stack, &backplane );
941 //FIXME: shouldn't we create a new source origin and radius for fast checks?
942 if ( !stack.source ) {
948 stack.source = VisChopWinding( prevstack->source, &stack, &backplane );
949 if ( !stack.source ) {
954 if ( !prevstack->pass ) { // the second leaf can only be blocked if coplanar
956 // mark the portal as visible
957 thread->base->portalvis[pnum >> 3] |= ( 1 << ( pnum & 7 ) );
959 RecursivePassagePortalFlow( p, thread, &stack );
963 #ifdef SEPERATORCACHE
964 if ( stack.numseperators[0] ) {
965 for ( n = 0; n < stack.numseperators[0]; n++ )
967 stack.pass = VisChopWinding( stack.pass, &stack, &stack.seperators[0][n] );
969 break; // target is not visible
972 if ( n < stack.numseperators[0] ) {
978 stack.pass = ClipToSeperators( prevstack->source, prevstack->pass, stack.pass, qfalse, &stack );
981 stack.pass = ClipToSeperators( stack.source, prevstack->pass, stack.pass, qfalse, &stack );
987 #ifdef SEPERATORCACHE
988 if ( stack.numseperators[1] ) {
989 for ( n = 0; n < stack.numseperators[1]; n++ )
991 stack.pass = VisChopWinding( stack.pass, &stack, &stack.seperators[1][n] );
993 break; // target is not visible
999 stack.pass = ClipToSeperators( prevstack->pass, prevstack->source, stack.pass, qtrue, &stack );
1002 stack.pass = ClipToSeperators( prevstack->pass, stack.source, stack.pass, qtrue, &stack );
1004 if ( !stack.pass ) {
1008 // mark the portal as visible
1009 thread->base->portalvis[pnum >> 3] |= ( 1 << ( pnum & 7 ) );
1011 // flow through it for real
1012 RecursivePassagePortalFlow( p, thread, &stack );
1023 void PassagePortalFlow( int portalnum ){
1029 p = sorted_portals[portalnum];
1032 p->status = stat_done;
1036 p->status = stat_working;
1038 memset( &data, 0, sizeof( data ) );
1041 data.pstack_head.portal = p;
1042 data.pstack_head.source = p->winding;
1043 data.pstack_head.portalplane = p->plane;
1044 data.pstack_head.depth = 0;
1045 for ( i = 0 ; i < portallongs ; i++ )
1046 ( (long *)data.pstack_head.mightsee )[i] = ( (long *)p->portalflood )[i];
1048 RecursivePassagePortalFlow( p, &data, &data.pstack_head );
1050 p->status = stat_done;
1053 c_can = CountBits (p->portalvis, numportals*2);
1055 Sys_FPrintf (SYS_VRB,"portal:%4i mightsee:%4i cansee:%4i (%i chains)\n",
1056 (int)(p - portals), c_might, c_can, data.c_chains);
1060 fixedWinding_t *PassageChopWinding( fixedWinding_t *in, fixedWinding_t *out, visPlane_t *split ){
1068 fixedWinding_t *neww;
1070 counts[0] = counts[1] = counts[2] = 0;
1072 // determine sides for each point
1073 for ( i = 0 ; i < in->numpoints ; i++ )
1075 dot = DotProduct( in->points[i], split->normal );
1078 if ( dot > ON_EPSILON ) {
1079 sides[i] = SIDE_FRONT;
1081 else if ( dot < -ON_EPSILON ) {
1082 sides[i] = SIDE_BACK;
1092 return in; // completely on front side
1099 sides[i] = sides[0];
1100 dists[i] = dists[0];
1104 neww->numpoints = 0;
1106 for ( i = 0 ; i < in->numpoints ; i++ )
1110 if ( neww->numpoints == MAX_POINTS_ON_FIXED_WINDING ) {
1111 return in; // can't chop -- fall back to original
1114 if ( sides[i] == SIDE_ON ) {
1115 VectorCopy( p1, neww->points[neww->numpoints] );
1120 if ( sides[i] == SIDE_FRONT ) {
1121 VectorCopy( p1, neww->points[neww->numpoints] );
1125 if ( sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i] ) {
1129 if ( neww->numpoints == MAX_POINTS_ON_FIXED_WINDING ) {
1130 return in; // can't chop -- fall back to original
1133 // generate a split point
1134 p2 = in->points[( i + 1 ) % in->numpoints];
1136 dot = dists[i] / ( dists[i] - dists[i + 1] );
1137 for ( j = 0 ; j < 3 ; j++ )
1138 { // avoid round off error when possible
1139 if ( split->normal[j] == 1 ) {
1140 mid[j] = split->dist;
1142 else if ( split->normal[j] == -1 ) {
1143 mid[j] = -split->dist;
1146 mid[j] = p1[j] + dot * ( p2[j] - p1[j] );
1150 VectorCopy( mid, neww->points[neww->numpoints] );
1162 int AddSeperators( fixedWinding_t *source, fixedWinding_t *pass, qboolean flipclip, visPlane_t *seperators, int maxseperators ){
1168 int counts[3], numseperators;
1172 // check all combinations
1173 for ( i = 0 ; i < source->numpoints ; i++ )
1175 l = ( i + 1 ) % source->numpoints;
1176 VectorSubtract( source->points[l], source->points[i], v1 );
1178 // find a vertex of pass that makes a plane that puts all of the
1179 // vertexes of pass on the front side and all of the vertexes of
1180 // source on the back side
1181 for ( j = 0 ; j < pass->numpoints ; j++ )
1183 VectorSubtract( pass->points[j], source->points[i], v2 );
1185 plane.normal[0] = v1[1] * v2[2] - v1[2] * v2[1];
1186 plane.normal[1] = v1[2] * v2[0] - v1[0] * v2[2];
1187 plane.normal[2] = v1[0] * v2[1] - v1[1] * v2[0];
1189 // if points don't make a valid plane, skip it
1191 length = plane.normal[0] * plane.normal[0]
1192 + plane.normal[1] * plane.normal[1]
1193 + plane.normal[2] * plane.normal[2];
1195 if ( length < ON_EPSILON ) {
1199 length = 1 / sqrt( length );
1201 plane.normal[0] *= length;
1202 plane.normal[1] *= length;
1203 plane.normal[2] *= length;
1205 plane.dist = DotProduct( pass->points[j], plane.normal );
1208 // find out which side of the generated seperating plane has the
1213 for ( k = 0 ; k < source->numpoints ; k++ )
1215 if ( k == i || k == l ) {
1218 d = DotProduct( source->points[k], plane.normal ) - plane.dist;
1219 if ( d < -ON_EPSILON ) { // source is on the negative side, so we want all
1220 // pass and target on the positive side
1224 else if ( d > ON_EPSILON ) { // source is on the positive side, so we want all
1225 // pass and target on the negative side
1230 if ( k == source->numpoints ) {
1231 continue; // planar with source portal
1234 fliptest = flipclip;
1237 // flip the normal if the source portal is backwards
1240 VectorSubtract( vec3_origin, plane.normal, plane.normal );
1241 plane.dist = -plane.dist;
1245 // if all of the pass portal points are now on the positive side,
1246 // this is the seperating plane
1248 counts[0] = counts[1] = counts[2] = 0;
1249 for ( k = 0 ; k < pass->numpoints ; k++ )
1254 d = DotProduct( pass->points[k], plane.normal ) - plane.dist;
1255 if ( d < -ON_EPSILON ) {
1258 else if ( d > ON_EPSILON ) {
1265 if ( k != pass->numpoints ) {
1266 continue; // points on negative side, not a seperating plane
1270 continue; // planar with seperating plane
1273 k = ( j + 1 ) % pass->numpoints;
1274 d = DotProduct( pass->points[k], plane.normal ) - plane.dist;
1275 if ( d < -ON_EPSILON ) {
1278 k = ( j + pass->numpoints - 1 ) % pass->numpoints;
1279 d = DotProduct( pass->points[k], plane.normal ) - plane.dist;
1280 if ( d < -ON_EPSILON ) {
1285 // flip the normal if we want the back side
1288 VectorSubtract( vec3_origin, plane.normal, plane.normal );
1289 plane.dist = -plane.dist;
1292 if ( numseperators >= maxseperators ) {
1293 Error( "max seperators" );
1295 seperators[numseperators] = plane;
1300 return numseperators;
1307 MrE: create passages from one portal to all the portals in the leaf the portal leads to
1308 every passage has a cansee bit string with all the portals that can be
1309 seen through the passage
1312 void CreatePassages( int portalnum ){
1313 int i, j, k, n, numseperators, numsee;
1315 vportal_t *portal, *p, *target;
1317 passage_t *passage, *lastpassage;
1318 visPlane_t seperators[MAX_SEPERATORS * 2];
1320 fixedWinding_t in, out, *res;
1323 portal = sorted_portals[portalnum];
1325 if ( portal->removed ) {
1326 portal->status = stat_done;
1331 leaf = &leafs[portal->leaf];
1332 for ( i = 0; i < leaf->numportals; i++ )
1334 target = leaf->portals[i];
1335 if ( target->removed ) {
1339 passage = (passage_t *) safe_malloc( sizeof( passage_t ) + portalbytes );
1340 memset( passage, 0, sizeof( passage_t ) + portalbytes );
1341 numseperators = AddSeperators( portal->winding, target->winding, qfalse, seperators, MAX_SEPERATORS * 2 );
1342 numseperators += AddSeperators( target->winding, portal->winding, qtrue, &seperators[numseperators], MAX_SEPERATORS * 2 - numseperators );
1344 passage->next = NULL;
1345 if ( lastpassage ) {
1346 lastpassage->next = passage;
1349 portal->passages = passage;
1351 lastpassage = passage;
1354 //create the passage->cansee
1355 for ( j = 0; j < numportals * 2; j++ )
1361 if ( !( target->portalflood[j >> 3] & ( 1 << ( j & 7 ) ) ) ) {
1364 if ( !( portal->portalflood[j >> 3] & ( 1 << ( j & 7 ) ) ) ) {
1367 for ( k = 0; k < numseperators; k++ )
1370 d = DotProduct( p->origin, seperators[k].normal ) - seperators[k].dist;
1371 //if completely at the back of the seperator plane
1372 if ( d < -p->radius + ON_EPSILON ) {
1376 for ( n = 0; n < w->numpoints; n++ )
1378 d = DotProduct( w->points[n], seperators[k].normal ) - seperators[k].dist;
1379 //if at the front of the seperator
1380 if ( d > ON_EPSILON ) {
1384 //if no points are at the front of the seperator
1385 if ( n >= w->numpoints ) {
1389 if ( k < numseperators ) {
1393 /* explitive deleted */
1396 /* ydnar: prefer correctness to stack overflow */
1397 //% memcpy( &in, p->winding, (int)((fixedWinding_t *)0)->points[p->winding->numpoints] );
1398 if ( p->winding->numpoints <= MAX_POINTS_ON_FIXED_WINDING ) {
1399 memcpy( &in, p->winding, (size_t) &( ( (fixedWinding_t*) 0 )->points[ p->winding->numpoints ] ) );
1402 memcpy( &in, p->winding, sizeof( fixedWinding_t ) );
1406 for ( k = 0; k < numseperators; k++ )
1408 /* ydnar: this is a shitty crutch */
1409 if ( in.numpoints > MAX_POINTS_ON_FIXED_WINDING ) {
1410 //% Sys_Printf( "[%d]", p->winding->numpoints );
1411 in.numpoints = MAX_POINTS_ON_FIXED_WINDING;
1414 res = PassageChopWinding( &in, &out, &seperators[ k ] );
1415 if ( res == &out ) {
1416 memcpy( &in, &out, sizeof( fixedWinding_t ) );
1420 if ( res == NULL ) {
1424 if ( k < numseperators ) {
1427 passage->cansee[j >> 3] |= ( 1 << ( j & 7 ) );
1433 void PassageMemory( void ){
1434 int i, j, totalmem, totalportals;
1435 vportal_t *portal, *target;
1440 for ( i = 0; i < numportals; i++ )
1442 portal = sorted_portals[i];
1443 if ( portal->removed ) {
1446 leaf = &leafs[portal->leaf];
1447 for ( j = 0; j < leaf->numportals; j++ )
1449 target = leaf->portals[j];
1450 if ( target->removed ) {
1453 totalmem += sizeof( passage_t ) + portalbytes;
1457 Sys_Printf( "%7i average number of passages per leaf\n", totalportals / numportals );
1458 Sys_Printf( "%7i MB required passage memory\n", totalmem >> 10 >> 10 );
1462 ===============================================================================
1464 This is a rough first-order aproximation that is used to trivially reject some
1465 of the final calculations.
1468 Calculates portalfront and portalflood bit vectors
1472 typedef struct passage_s
1474 struct passage_s *next;
1475 struct portal_s *to;
1476 stryct sep_s *seperators;
1480 typedef struct portal_s
1482 struct passage_s *passages;
1483 int leaf; // leaf portal faces into
1491 calc portal visibility
1497 for a portal to be visible to a passage, it must be on the front of
1498 all seperating planes, and both portals must be behind the new portal
1500 ===============================================================================
1512 void SimpleFlood( vportal_t *srcportal, int leafnum ){
1518 leaf = &leafs[leafnum];
1520 for ( i = 0 ; i < leaf->numportals ; i++ )
1522 p = leaf->portals[i];
1527 if ( !( srcportal->portalfront[pnum >> 3] & ( 1 << ( pnum & 7 ) ) ) ) {
1531 if ( srcportal->portalflood[pnum >> 3] & ( 1 << ( pnum & 7 ) ) ) {
1535 srcportal->portalflood[pnum >> 3] |= ( 1 << ( pnum & 7 ) );
1537 SimpleFlood( srcportal, p->leaf );
1546 void BasePortalVis( int portalnum ){
1554 p = portals + portalnum;
1560 p->portalfront = safe_malloc( portalbytes );
1561 memset( p->portalfront, 0, portalbytes );
1563 p->portalflood = safe_malloc( portalbytes );
1564 memset( p->portalflood, 0, portalbytes );
1566 p->portalvis = safe_malloc( portalbytes );
1567 memset( p->portalvis, 0, portalbytes );
1569 for ( j = 0, tp = portals ; j < numportals * 2 ; j++, tp++ )
1571 if ( j == portalnum ) {
1574 if ( tp->removed ) {
1578 /* ydnar: this is old farplane vis code from mre */
1580 if (farplanedist >= 0)
1583 VectorSubtract(p->origin, tp->origin, dir);
1584 if (VectorLength(dir) > farplanedist - p->radius - tp->radius)
1589 /* ydnar: this is known-to-be-working farplane code */
1590 if ( !p->sky && !tp->sky && farPlaneDist > 0.0f ) {
1591 VectorSubtract( p->origin, tp->origin, dir );
1592 if ( VectorLength( dir ) - p->radius - tp->radius > farPlaneDist ) {
1599 for ( k = 0 ; k < w->numpoints ; k++ )
1601 d = DotProduct( w->points[k], p->plane.normal )
1603 if ( d > ON_EPSILON ) {
1607 if ( k == w->numpoints ) {
1608 continue; // no points on front
1612 for ( k = 0 ; k < w->numpoints ; k++ )
1614 d = DotProduct( w->points[k], tp->plane.normal )
1616 if ( d < -ON_EPSILON ) {
1620 if ( k == w->numpoints ) {
1621 continue; // no points on front
1624 p->portalfront[j >> 3] |= ( 1 << ( j & 7 ) );
1627 SimpleFlood( p, p->leaf );
1629 p->nummightsee = CountBits( p->portalflood, numportals * 2 );
1630 // Sys_Printf ("portal %i: %i mightsee\n", portalnum, p->nummightsee);
1631 c_flood += p->nummightsee;
1639 ===============================================================================
1641 This is a second order aproximation
1643 Calculates portalvis bit vector
1647 ===============================================================================
1652 RecursiveLeafBitFlow
1656 void RecursiveLeafBitFlow( int leafnum, byte *mightsee, byte *cansee ){
1662 byte newmight[MAX_PORTALS / 8];
1664 leaf = &leafs[leafnum];
1666 // check all portals for flowing into other leafs
1667 for ( i = 0 ; i < leaf->numportals ; i++ )
1669 p = leaf->portals[i];
1675 // if some previous portal can't see it, skip
1676 if ( !( mightsee[pnum >> 3] & ( 1 << ( pnum & 7 ) ) ) ) {
1680 // if this portal can see some portals we mightsee, recurse
1682 for ( j = 0 ; j < portallongs ; j++ )
1684 ( (long *)newmight )[j] = ( (long *)mightsee )[j]
1685 & ( (long *)p->portalflood )[j];
1686 more |= ( (long *)newmight )[j] & ~( (long *)cansee )[j];
1690 continue; // can't see anything new
1693 cansee[pnum >> 3] |= ( 1 << ( pnum & 7 ) );
1695 RecursiveLeafBitFlow( p->leaf, newmight, cansee );
1704 void BetterPortalVis( int portalnum ){
1707 p = portals + portalnum;
1713 RecursiveLeafBitFlow( p->leaf, p->portalflood, p->portalvis );
1715 // build leaf vis information
1716 p->nummightsee = CountBits( p->portalvis, numportals * 2 );
1717 c_vis += p->nummightsee;