2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
\r
5 This file is part of GtkRadiant.
\r
7 GtkRadiant is free software; you can redistribute it and/or modify
\r
8 it under the terms of the GNU General Public License as published by
\r
9 the Free Software Foundation; either version 2 of the License, or
\r
10 (at your option) any later version.
\r
12 GtkRadiant is distributed in the hope that it will be useful,
\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
15 GNU General Public License for more details.
\r
17 You should have received a copy of the GNU General Public License
\r
18 along with GtkRadiant; if not, write to the Free Software
\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\r
25 each portal will have a list of all possible to see from first portal
\r
27 if (!thread->portalmightsee[portalnum])
\r
31 for p2 = all other portals in leaf
\r
32 get sperating planes
\r
33 for all portals that might be seen by p2
\r
34 mark as unseen if not present in seperating plane
\r
35 flood fill a new mightsee
\r
36 save as passagemightsee
\r
39 void CalcMightSee (leaf_t *leaf,
\r
42 int CountBits (byte *bits, int numbits)
\r
48 for (i=0 ; i<numbits ; i++)
\r
49 if (bits[i>>3] & (1<<(i&7)) )
\r
56 int c_portalskip, c_leafskip;
\r
57 int c_vistest, c_mighttest;
\r
59 int c_chop, c_nochop;
\r
63 void CheckStack (leaf_t *leaf, threaddata_t *thread)
\r
67 for (p=thread->pstack_head.next ; p ; p=p->next)
\r
70 if (p->leaf == leaf)
\r
71 Error ("CheckStack: leaf recursion");
\r
72 for (p2=thread->pstack_head.next ; p2 != p ; p2=p2->next)
\r
73 if (p2->leaf == p->leaf)
\r
74 Error ("CheckStack: late leaf recursion");
\r
80 winding_t *AllocStackWinding (pstack_t *stack)
\r
84 for (i=0 ; i<3 ; i++)
\r
86 if (stack->freewindings[i])
\r
88 stack->freewindings[i] = 0;
\r
89 return &stack->windings[i];
\r
93 Error ("AllocStackWinding: failed");
\r
98 void FreeStackWinding (winding_t *w, pstack_t *stack)
\r
102 i = w - stack->windings;
\r
105 return; // not from local
\r
107 if (stack->freewindings[i])
\r
108 Error ("FreeStackWinding: allready free");
\r
109 stack->freewindings[i] = 1;
\r
118 winding_t *Vis_ChopWinding (winding_t *in, pstack_t *stack, plane_t *split)
\r
129 counts[0] = counts[1] = counts[2] = 0;
\r
131 // determine sides for each point
\r
132 for (i=0 ; i<in->numpoints ; i++)
\r
134 dot = DotProduct (in->points[i], split->normal);
\r
135 dot -= split->dist;
\r
137 if (dot > ON_EPSILON)
\r
138 sides[i] = SIDE_FRONT;
\r
139 else if (dot < -ON_EPSILON)
\r
140 sides[i] = SIDE_BACK;
\r
143 sides[i] = SIDE_ON;
\r
145 counts[sides[i]]++;
\r
149 return in; // completely on front side
\r
153 FreeStackWinding (in, stack);
\r
157 sides[i] = sides[0];
\r
158 dists[i] = dists[0];
\r
160 neww = AllocStackWinding (stack);
\r
162 neww->numpoints = 0;
\r
164 for (i=0 ; i<in->numpoints ; i++)
\r
166 p1 = in->points[i];
\r
168 if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING)
\r
170 FreeStackWinding (neww, stack);
\r
171 return in; // can't chop -- fall back to original
\r
174 if (sides[i] == SIDE_ON)
\r
176 VectorCopy (p1, neww->points[neww->numpoints]);
\r
181 if (sides[i] == SIDE_FRONT)
\r
183 VectorCopy (p1, neww->points[neww->numpoints]);
\r
187 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
\r
190 if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING)
\r
192 FreeStackWinding (neww, stack);
\r
193 return in; // can't chop -- fall back to original
\r
196 // generate a split point
\r
197 p2 = in->points[(i+1)%in->numpoints];
\r
199 dot = dists[i] / (dists[i]-dists[i+1]);
\r
200 for (j=0 ; j<3 ; j++)
\r
201 { // avoid round off error when possible
\r
202 if (split->normal[j] == 1)
\r
203 mid[j] = split->dist;
\r
204 else if (split->normal[j] == -1)
\r
205 mid[j] = -split->dist;
\r
207 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
\r
210 VectorCopy (mid, neww->points[neww->numpoints]);
\r
214 // free the original winding
\r
215 FreeStackWinding (in, stack);
\r
225 Source, pass, and target are an ordering of portals.
\r
227 Generates seperating planes canidates by taking two points from source and one
\r
228 point from pass, and clips target by them.
\r
230 If target is totally clipped away, that portal can not be seen through.
\r
232 Normal clip keeps target on the same side as pass, which is correct if the
\r
233 order goes source, pass, target. If the order goes pass, source, target then
\r
234 flipclip should be set.
\r
237 winding_t *ClipToSeperators (winding_t *source, winding_t *pass, winding_t *target, qboolean flipclip, pstack_t *stack)
\r
247 // check all combinations
\r
248 for (i=0 ; i<source->numpoints ; i++)
\r
250 l = (i+1)%source->numpoints;
\r
251 VectorSubtract (source->points[l] , source->points[i], v1);
\r
253 // fing a vertex of pass that makes a plane that puts all of the
\r
254 // vertexes of pass on the front side and all of the vertexes of
\r
255 // source on the back side
\r
256 for (j=0 ; j<pass->numpoints ; j++)
\r
258 VectorSubtract (pass->points[j], source->points[i], v2);
\r
260 plane.normal[0] = v1[1]*v2[2] - v1[2]*v2[1];
\r
261 plane.normal[1] = v1[2]*v2[0] - v1[0]*v2[2];
\r
262 plane.normal[2] = v1[0]*v2[1] - v1[1]*v2[0];
\r
264 // if points don't make a valid plane, skip it
\r
266 length = plane.normal[0] * plane.normal[0]
\r
267 + plane.normal[1] * plane.normal[1]
\r
268 + plane.normal[2] * plane.normal[2];
\r
270 if (length < ON_EPSILON)
\r
273 length = 1/sqrt(length);
\r
275 plane.normal[0] *= length;
\r
276 plane.normal[1] *= length;
\r
277 plane.normal[2] *= length;
\r
279 plane.dist = DotProduct (pass->points[j], plane.normal);
\r
282 // find out which side of the generated seperating plane has the
\r
287 for (k=0 ; k<source->numpoints ; k++)
\r
289 if (k == i || k == l)
\r
291 d = DotProduct (source->points[k], plane.normal) - plane.dist;
\r
292 if (d < -ON_EPSILON)
\r
293 { // source is on the negative side, so we want all
\r
294 // pass and target on the positive side
\r
298 else if (d > ON_EPSILON)
\r
299 { // source is on the positive side, so we want all
\r
300 // pass and target on the negative side
\r
305 if (k == source->numpoints)
\r
306 continue; // planar with source portal
\r
308 fliptest = flipclip;
\r
311 // flip the normal if the source portal is backwards
\r
315 VectorSubtract (vec3_origin, plane.normal, plane.normal);
\r
316 plane.dist = -plane.dist;
\r
320 // if all of the pass portal points are now on the positive side,
\r
321 // this is the seperating plane
\r
323 counts[0] = counts[1] = counts[2] = 0;
\r
324 for (k=0 ; k<pass->numpoints ; k++)
\r
328 d = DotProduct (pass->points[k], plane.normal) - plane.dist;
\r
329 if (d < -ON_EPSILON)
\r
331 else if (d > ON_EPSILON)
\r
336 if (k != pass->numpoints)
\r
337 continue; // points on negative side, not a seperating plane
\r
340 continue; // planar with seperating plane
\r
342 k = (j+1)%pass->numpoints;
\r
343 d = DotProduct (pass->points[k], plane.normal) - plane.dist;
\r
344 if (d < -ON_EPSILON)
\r
346 k = (j+pass->numpoints-1)%pass->numpoints;
\r
347 d = DotProduct (pass->points[k], plane.normal) - plane.dist;
\r
348 if (d < -ON_EPSILON)
\r
352 // flip the normal if we want the back side
\r
356 VectorSubtract (vec3_origin, plane.normal, plane.normal);
\r
357 plane.dist = -plane.dist;
\r
361 // clip target by the seperating plane
\r
363 target = Vis_ChopWinding (target, stack, &plane);
\r
365 return NULL; // target is not visible
\r
378 Flood fill through the leafs
\r
379 If src_portal is NULL, this is the originating leaf
\r
382 void RecursiveLeafFlow (int leafnum, threaddata_t *thread, pstack_t *prevstack)
\r
389 long *test, *might, *vis, more;
\r
392 thread->c_chains++;
\r
394 leaf = &leafs[leafnum];
\r
395 // CheckStack (leaf, thread);
\r
397 prevstack->next = &stack;
\r
401 stack.portal = NULL;
\r
403 might = (long *)stack.mightsee;
\r
404 vis = (long *)thread->base->portalvis;
\r
406 // check all portals for flowing into other leafs
\r
407 for (i=0 ; i<leaf->numportals ; i++)
\r
409 p = leaf->portals[i];
\r
410 pnum = p - portals;
\r
412 if ( ! (prevstack->mightsee[pnum >> 3] & (1<<(pnum&7)) ) )
\r
414 continue; // can't possibly see it
\r
417 // if the portal can't see anything we haven't allready seen, skip it
\r
418 if (p->status == stat_done)
\r
420 test = (long *)p->portalvis;
\r
424 test = (long *)p->portalflood;
\r
428 for (j=0 ; j<portallongs ; j++)
\r
430 might[j] = ((long *)prevstack->mightsee)[j] & test[j];
\r
431 more |= (might[j] & ~vis[j]);
\r
435 (thread->base->portalvis[pnum>>3] & (1<<(pnum&7))) )
\r
436 { // can't see anything new
\r
440 // get plane of portal, point normal into the neighbor leaf
\r
441 stack.portalplane = p->plane;
\r
442 VectorSubtract (vec3_origin, p->plane.normal, backplane.normal);
\r
443 backplane.dist = -p->plane.dist;
\r
445 // c_portalcheck++;
\r
449 stack.freewindings[0] = 1;
\r
450 stack.freewindings[1] = 1;
\r
451 stack.freewindings[2] = 1;
\r
457 d = DotProduct (p->origin, thread->pstack_head.portalplane.normal);
\r
458 d -= thread->pstack_head.portalplane.dist;
\r
459 if (d < -p->radius)
\r
463 else if (d > p->radius)
\r
465 stack.pass = p->winding;
\r
469 stack.pass = Vis_ChopWinding (p->winding, &stack, &thread->pstack_head.portalplane);
\r
475 stack.pass = Vis_ChopWinding (p->winding, &stack, &thread->pstack_head.portalplane);
\r
485 d = DotProduct (thread->base->origin, p->plane.normal);
\r
486 d -= p->plane.dist;
\r
491 else if (d < -p->radius)
\r
493 stack.source = prevstack->source;
\r
497 stack.source = Vis_ChopWinding (prevstack->source, &stack, &backplane);
\r
503 stack.source = Vis_ChopWinding (prevstack->source, &stack, &backplane);
\r
508 if (!prevstack->pass)
\r
509 { // the second leaf can only be blocked if coplanar
\r
511 // mark the portal as visible
\r
512 thread->base->portalvis[pnum>>3] |= (1<<(pnum&7));
\r
514 RecursiveLeafFlow (p->leaf, thread, &stack);
\r
518 stack.pass = ClipToSeperators (stack.source, prevstack->pass, stack.pass, false, &stack);
\r
522 stack.pass = ClipToSeperators (prevstack->pass, stack.source, stack.pass, true, &stack);
\r
526 // mark the portal as visible
\r
527 thread->base->portalvis[pnum>>3] |= (1<<(pnum&7));
\r
529 // flow through it for real
\r
530 RecursiveLeafFlow (p->leaf, thread, &stack);
\r
539 generates the portalvis bit vector
\r
542 void PortalFlow (int portalnum)
\r
547 int c_might, c_can;
\r
549 p = sorted_portals[portalnum];
\r
550 p->status = stat_working;
\r
552 c_might = CountBits (p->portalflood, numportals*2);
\r
554 memset (&data, 0, sizeof(data));
\r
557 data.pstack_head.portal = p;
\r
558 data.pstack_head.source = p->winding;
\r
559 data.pstack_head.portalplane = p->plane;
\r
560 for (i=0 ; i<portallongs ; i++)
\r
561 ((long *)data.pstack_head.mightsee)[i] = ((long *)p->portalflood)[i];
\r
562 RecursiveLeafFlow (p->leaf, &data, &data.pstack_head);
\r
564 p->status = stat_done;
\r
566 c_can = CountBits (p->portalvis, numportals*2);
\r
568 Sys_FPrintf ( SYS_VRB, "portal:%4i mightsee:%4i cansee:%4i (%i chains)\n",
\r
569 (int)(p - portals), c_might, c_can, data.c_chains);
\r
574 ===============================================================================
\r
576 This is a rough first-order aproximation that is used to trivially reject some
\r
577 of the final calculations.
\r
580 Calculates portalfront and portalflood bit vectors
\r
584 typedef struct passage_s
\r
586 struct passage_s *next;
\r
587 struct portal_s *to;
\r
588 stryct sep_s *seperators;
\r
592 typedef struct portal_s
\r
594 struct passage_s *passages;
\r
595 int leaf; // leaf portal faces into
\r
598 leaf = portal->leaf
\r
603 calc portal visibility
\r
609 for a portal to be visible to a passage, it must be on the front of
\r
610 all seperating planes, and both portals must be behind the mew portal
\r
612 ===============================================================================
\r
615 int c_flood, c_vis;
\r
624 void SimpleFlood (portal_t *srcportal, int leafnum)
\r
631 leaf = &leafs[leafnum];
\r
633 for (i=0 ; i<leaf->numportals ; i++)
\r
635 p = leaf->portals[i];
\r
636 pnum = p - portals;
\r
637 if ( ! (srcportal->portalfront[pnum>>3] & (1<<(pnum&7)) ) )
\r
640 if (srcportal->portalflood[pnum>>3] & (1<<(pnum&7)) )
\r
643 srcportal->portalflood[pnum>>3] |= (1<<(pnum&7));
\r
645 SimpleFlood (srcportal, p->leaf);
\r
654 void BasePortalVis (int portalnum)
\r
661 p = portals+portalnum;
\r
663 p->portalfront = malloc (portalbytes);
\r
664 memset (p->portalfront, 0, portalbytes);
\r
666 p->portalflood = malloc (portalbytes);
\r
667 memset (p->portalflood, 0, portalbytes);
\r
669 p->portalvis = malloc (portalbytes);
\r
670 memset (p->portalvis, 0, portalbytes);
\r
672 for (j=0, tp = portals ; j<numportals*2 ; j++, tp++)
\r
674 if (j == portalnum)
\r
677 for (k=0 ; k<w->numpoints ; k++)
\r
679 d = DotProduct (w->points[k], p->plane.normal)
\r
681 if (d > ON_EPSILON)
\r
684 if (k == w->numpoints)
\r
685 continue; // no points on front
\r
688 for (k=0 ; k<w->numpoints ; k++)
\r
690 d = DotProduct (w->points[k], tp->plane.normal)
\r
692 if (d < -ON_EPSILON)
\r
695 if (k == w->numpoints)
\r
696 continue; // no points on front
\r
698 p->portalfront[j>>3] |= (1<<(j&7));
\r
701 SimpleFlood (p, p->leaf);
\r
703 p->nummightsee = CountBits (p->portalflood, numportals*2);
\r
704 // printf ("portal %i: %i mightsee\n", portalnum, p->nummightsee);
\r
705 c_flood += p->nummightsee;
\r
713 ===============================================================================
\r
715 This is a second order aproximation
\r
717 Calculates portalvis bit vector
\r
721 ===============================================================================
\r
726 RecursiveLeafBitFlow
\r
730 void RecursiveLeafBitFlow (int leafnum, byte *mightsee, byte *cansee)
\r
737 byte newmight[MAX_PORTALS/8];
\r
739 leaf = &leafs[leafnum];
\r
741 // check all portals for flowing into other leafs
\r
742 for (i=0 ; i<leaf->numportals ; i++)
\r
744 p = leaf->portals[i];
\r
745 pnum = p - portals;
\r
747 // if some previous portal can't see it, skip
\r
748 if (! (mightsee[pnum>>3] & (1<<(pnum&7)) ) )
\r
751 // if this portal can see some portals we mightsee, recurse
\r
753 for (j=0 ; j<portallongs ; j++)
\r
755 ((long *)newmight)[j] = ((long *)mightsee)[j]
\r
756 & ((long *)p->portalflood)[j];
\r
757 more |= ((long *)newmight)[j] & ~((long *)cansee)[j];
\r
761 continue; // can't see anything new
\r
763 cansee[pnum>>3] |= (1<<(pnum&7));
\r
765 RecursiveLeafBitFlow (p->leaf, newmight, cansee);
\r
774 void BetterPortalVis (int portalnum)
\r
778 p = portals+portalnum;
\r
780 RecursiveLeafBitFlow (p->leaf, p->portalflood, p->portalvis);
\r
782 // build leaf vis information
\r
783 p->nummightsee = CountBits (p->portalvis, numportals*2);
\r
784 c_vis += p->nummightsee;
\r