2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
26 ===========================================================================
29 #include "../../../idlib/precompiled.h"
35 interAreaPortal_t interAreaPortals[MAX_INTER_AREA_PORTALS];
36 int numInterAreaPortals;
47 uPortal_t *AllocPortal (void)
52 if (c_active_portals > c_peak_portals)
53 c_peak_portals = c_active_portals;
55 p = (uPortal_t *)Mem_Alloc (sizeof(uPortal_t ));
56 memset (p, 0, sizeof(uPortal_t ));
62 void FreePortal (uPortal_t *p)
70 //==============================================================
76 Returns true if the portal has non-opaque leafs on both sides
79 static bool Portal_Passable( uPortal_t *p ) {
81 return false; // to global outsideleaf
84 if (p->nodes[0]->planenum != PLANENUM_LEAF
85 || p->nodes[1]->planenum != PLANENUM_LEAF) {
86 common->Error( "Portal_EntityFlood: not a leaf");
89 if ( !p->nodes[0]->opaque && !p->nodes[1]->opaque ) {
97 //=============================================================================
106 void AddPortalToNodes (uPortal_t *p, node_t *front, node_t *back) {
107 if (p->nodes[0] || p->nodes[1]) {
108 common->Error( "AddPortalToNode: allready included");
112 p->next[0] = front->portals;
116 p->next[1] = back->portals;
126 void RemovePortalFromNode (uPortal_t *portal, node_t *l)
130 // remove reference to the current portal
136 common->Error( "RemovePortalFromNode: portal not in leaf");
141 if (t->nodes[0] == l)
143 else if (t->nodes[1] == l)
146 common->Error( "RemovePortalFromNode: portal not bounding leaf");
149 if ( portal->nodes[0] == l ) {
150 *pp = portal->next[0];
151 portal->nodes[0] = NULL;
152 } else if ( portal->nodes[1] == l ) {
153 *pp = portal->next[1];
154 portal->nodes[1] = NULL;
156 common->Error( "RemovePortalFromNode: mislinked" );
160 //============================================================================
162 void PrintPortal (uPortal_t *p)
168 for ( i = 0; i < w->GetNumPoints(); i++ )
169 common->Printf("(%5.0f,%5.0f,%5.0f)\n",(*w)[i][0], (*w)[i][1], (*w)[i][2]);
176 The created portals will face the global outside_node
180 static void MakeHeadnodePortals( tree_t *tree ) {
183 uPortal_t *p, *portals[6];
184 idPlane bplanes[6], *pl;
187 node = tree->headnode;
189 tree->outside_node.planenum = PLANENUM_LEAF;
190 tree->outside_node.brushlist = NULL;
191 tree->outside_node.portals = NULL;
192 tree->outside_node.opaque = false;
194 // if no nodes, don't go any farther
195 if ( node->planenum == PLANENUM_LEAF ) {
199 // pad with some space so there will never be null volume leafs
200 for (i=0 ; i<3 ; i++) {
201 bounds[0][i] = tree->bounds[0][i] - SIDESPACE;
202 bounds[1][i] = tree->bounds[1][i] + SIDESPACE;
203 if ( bounds[0][i] >= bounds[1][i] ) {
204 common->Error( "Backwards tree volume" );
208 for (i=0 ; i<3 ; i++) {
209 for (j=0 ; j<2 ; j++) {
216 memset (pl, 0, sizeof(*pl));
219 (*pl)[3] = bounds[j][i];
222 (*pl)[3] = -bounds[j][i];
225 p->winding = new idWinding( *pl );
226 AddPortalToNodes (p, node, &tree->outside_node);
230 // clip the basewindings by all the other planes
231 for (i=0 ; i<6 ; i++) {
232 for (j=0 ; j<6 ; j++) {
236 portals[i]->winding = portals[i]->winding->Clip( bplanes[j], ON_EPSILON );
241 //===================================================
249 #define BASE_WINDING_EPSILON 0.001f
250 #define SPLIT_WINDING_EPSILON 0.001f
252 idWinding *BaseWindingForNode (node_t *node) {
256 w = new idWinding( dmapGlobals.mapPlanes[node->planenum] );
258 // clip by all the parents
259 for ( n = node->parent ; n && w ; ) {
260 idPlane &plane = dmapGlobals.mapPlanes[n->planenum];
262 if ( n->children[0] == node ) {
264 w = w->Clip( plane, BASE_WINDING_EPSILON );
267 idPlane back = -plane;
268 w = w->Clip( back, BASE_WINDING_EPSILON );
277 //============================================================
283 create the new portal by taking the full plane winding for the cutting plane
284 and clipping it by all of parents of this node
287 static void MakeNodePortal( node_t *node ) {
288 uPortal_t *new_portal, *p;
293 w = BaseWindingForNode (node);
295 // clip the portal by all the other portals in the node
296 for (p = node->portals ; p && w; p = p->next[side])
300 if (p->nodes[0] == node)
305 else if (p->nodes[1] == node)
311 common->Error( "CutNodePortals_r: mislinked portal");
312 side = 0; // quiet a compiler warning
315 w = w->Clip( plane, CLIP_EPSILON );
331 new_portal = AllocPortal ();
332 new_portal->plane = dmapGlobals.mapPlanes[node->planenum];
333 new_portal->onnode = node;
334 new_portal->winding = w;
335 AddPortalToNodes (new_portal, node->children[0], node->children[1]);
343 Move or split the portals that bound node so that the node's
344 children have portals instead of node.
347 static void SplitNodePortals( node_t *node ) {
348 uPortal_t *p, *next_portal, *new_portal;
349 node_t *f, *b, *other_node;
352 idWinding *frontwinding, *backwinding;
354 plane = &dmapGlobals.mapPlanes[node->planenum];
355 f = node->children[0];
356 b = node->children[1];
358 for ( p = node->portals ; p ; p = next_portal ) {
359 if (p->nodes[0] == node ) {
361 } else if ( p->nodes[1] == node ) {
364 common->Error( "SplitNodePortals: mislinked portal" );
365 side = 0; // quiet a compiler warning
367 next_portal = p->next[side];
369 other_node = p->nodes[!side];
370 RemovePortalFromNode (p, p->nodes[0]);
371 RemovePortalFromNode (p, p->nodes[1]);
374 // cut the portal into two portals, one on each side of the cut plane
376 p->winding->Split( *plane, SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
378 if ( frontwinding && frontwinding->IsTiny() )
385 if ( backwinding && backwinding->IsTiny() )
392 if ( !frontwinding && !backwinding )
393 { // tiny windings on both sides
401 AddPortalToNodes (p, b, other_node);
403 AddPortalToNodes (p, other_node, b);
410 AddPortalToNodes (p, f, other_node);
412 AddPortalToNodes (p, other_node, f);
416 // the winding is split
417 new_portal = AllocPortal ();
419 new_portal->winding = backwinding;
421 p->winding = frontwinding;
425 AddPortalToNodes (p, f, other_node);
426 AddPortalToNodes (new_portal, b, other_node);
430 AddPortalToNodes (p, other_node, f);
431 AddPortalToNodes (new_portal, other_node, b);
435 node->portals = NULL;
444 void CalcNodeBounds (node_t *node)
450 // calc mins/maxs for both leafs and nodes
451 node->bounds.Clear();
452 for (p = node->portals ; p ; p = p->next[s]) {
453 s = (p->nodes[1] == node);
454 for ( i = 0; i < p->winding->GetNumPoints(); i++ ) {
455 node->bounds.AddPoint( (*p->winding)[i].ToVec3() );
466 void MakeTreePortals_r (node_t *node)
470 CalcNodeBounds( node );
472 if ( node->bounds[0][0] >= node->bounds[1][0]) {
473 common->Warning( "node without a volume" );
476 for ( i = 0; i < 3; i++ ) {
477 if ( node->bounds[0][i] < MIN_WORLD_COORD || node->bounds[1][i] > MAX_WORLD_COORD ) {
478 common->Warning( "node with unbounded volume");
482 if ( node->planenum == PLANENUM_LEAF ) {
486 MakeNodePortal (node);
487 SplitNodePortals (node);
489 MakeTreePortals_r (node->children[0]);
490 MakeTreePortals_r (node->children[1]);
498 void MakeTreePortals (tree_t *tree)
500 common->Printf( "----- MakeTreePortals -----\n");
501 MakeHeadnodePortals (tree);
502 MakeTreePortals_r (tree->headnode);
506 =========================================================
510 =========================================================
520 void FloodPortals_r (node_t *node, int dist) {
524 if ( node->occupied ) {
528 if ( node->opaque ) {
533 node->occupied = dist;
535 for (p=node->portals ; p ; p = p->next[s]) {
536 s = (p->nodes[1] == node);
537 FloodPortals_r (p->nodes[!s], dist+1);
546 bool PlaceOccupant( node_t *headnode, idVec3 origin, uEntity_t *occupant ) {
551 // find the leaf to start in
553 while ( node->planenum != PLANENUM_LEAF ) {
554 plane = &dmapGlobals.mapPlanes[node->planenum];
555 d = plane->Distance( origin );
557 node = node->children[0];
559 node = node->children[1];
563 if ( node->opaque ) {
566 node->occupant = occupant;
568 FloodPortals_r (node, 1);
577 Marks all nodes that can be reached by entites
580 bool FloodEntities( tree_t *tree ) {
587 headnode = tree->headnode;
588 common->Printf ("--- FloodEntities ---\n");
590 tree->outside_node.occupied = 0;
593 bool errorShown = false;
594 for (i=1 ; i<dmapGlobals.num_entities ; i++) {
597 mapEnt = dmapGlobals.uEntities[i].mapEntity;
598 if ( !mapEnt->epairs.GetVector( "origin", "", origin) ) {
602 // any entity can have "noFlood" set to skip it
603 if ( mapEnt->epairs.GetString( "noFlood", "", &cl ) ) {
607 mapEnt->epairs.GetString( "classname", "", &cl );
609 if ( !strcmp( cl, "light" ) ) {
612 // don't place lights that have a light_start field, because they can still
613 // be valid if their origin is outside the world
614 mapEnt->epairs.GetString( "light_start", "", &v);
619 // don't place fog lights, because they often
620 // have origins outside the light
621 mapEnt->epairs.GetString( "texture", "", &v);
623 const idMaterial *mat = declManager->FindMaterial( v );
624 if ( mat->IsFogLight() ) {
630 if (PlaceOccupant (headnode, origin, &dmapGlobals.uEntities[i])) {
634 if (tree->outside_node.occupied && !errorShown) {
636 common->Printf("Leak on entity # %d\n", i);
639 mapEnt->epairs.GetString( "classname", "", &p);
640 common->Printf("Entity classname was: %s\n", p);
641 mapEnt->epairs.GetString( "name", "", &p);
642 common->Printf("Entity name was: %s\n", p);
644 if ( mapEnt->epairs.GetVector( "origin", "", origin)) {
645 common->Printf("Entity origin is: %f %f %f\n\n\n", origin.x, origin.y, origin.z);
650 common->Printf("%5i flooded leafs\n", c_floodedleafs );
654 common->Printf ("no entities in open -- no filling\n");
656 else if (tree->outside_node.occupied)
658 common->Printf ("entity reached from outside -- no filling\n");
661 return (bool)(inside && !tree->outside_node.occupied);
665 =========================================================
669 =========================================================
673 static int c_areaFloods;
680 static side_t *FindSideForPortal( uPortal_t *p ) {
686 // scan both bordering nodes brush lists for a portal brush
687 // that shares the plane
688 for ( i = 0 ; i < 2 ; i++ ) {
690 for ( b = node->brushlist ; b ; b = b->next ) {
691 if ( !( b->contents & CONTENTS_AREAPORTAL ) ) {
695 for ( j = 0 ; j < orig->numsides ; j++ ) {
697 if ( !s->visibleHull ) {
700 if ( !( s->material->GetContentFlags() & CONTENTS_AREAPORTAL ) ) {
703 if ( ( s->planenum & ~1 ) != ( p->onnode->planenum & ~1 ) ) {
706 // remove the visible hull from any other portal sides of this portal brush
707 for ( k = 0; k < orig->numsides; k++ ) {
711 s2 = orig->sides + k;
712 if ( s2->visibleHull == NULL ) {
715 if ( !( s2->material->GetContentFlags() & CONTENTS_AREAPORTAL ) ) {
718 common->Warning( "brush has multiple area portal sides at %s", s2->visibleHull->GetCenter().ToString() );
719 delete s2->visibleHull;
720 s2->visibleHull = NULL;
734 void FloodAreas_r (node_t *node)
739 if ( node->area != -1 ) {
740 return; // allready got it
742 if ( node->opaque ) {
747 node->area = c_areas;
749 for ( p=node->portals ; p ; p = p->next[s] ) {
752 s = (p->nodes[1] == node);
753 other = p->nodes[!s];
755 if ( !Portal_Passable(p) ) {
759 // can't flood through an area portal
760 if ( FindSideForPortal( p ) ) {
764 FloodAreas_r( other );
772 Just decend the tree, and for each node that hasn't had an
773 area set, flood fill out from there
776 void FindAreas_r( node_t *node ) {
777 if ( node->planenum != PLANENUM_LEAF ) {
778 FindAreas_r (node->children[0]);
779 FindAreas_r (node->children[1]);
783 if ( node->opaque ) {
787 if ( node->area != -1 ) {
788 return; // allready got it
793 common->Printf( "area %i has %i leafs\n", c_areas, c_areaFloods );
802 void CheckAreas_r( node_t *node ) {
803 if ( node->planenum != PLANENUM_LEAF ) {
804 CheckAreas_r (node->children[0]);
805 CheckAreas_r (node->children[1]);
808 if ( !node->opaque && node->area < 0 ) {
809 common->Error( "CheckAreas_r: area = %i", node->area );
817 Set all the areas to -1 before filling
820 void ClearAreas_r( node_t *node ) {
821 if ( node->planenum != PLANENUM_LEAF ) {
822 ClearAreas_r (node->children[0]);
823 ClearAreas_r (node->children[1]);
829 //=============================================================
834 FindInterAreaPortals_r
838 static void FindInterAreaPortals_r( node_t *node ) {
843 interAreaPortal_t *iap;
846 if ( node->planenum != PLANENUM_LEAF ) {
847 FindInterAreaPortals_r( node->children[0] );
848 FindInterAreaPortals_r( node->children[1] );
852 if ( node->opaque ) {
856 for ( p=node->portals ; p ; p = p->next[s] ) {
859 s = (p->nodes[1] == node);
860 other = p->nodes[!s];
862 if ( other->opaque ) {
866 // only report areas going from lower number to higher number
867 // so we don't report the portal twice
868 if ( other->area <= node->area ) {
872 side = FindSideForPortal( p );
875 common->Warning( "FindSideForPortal failed at %s", p->winding->GetCenter().ToString() );
878 w = side->visibleHull;
883 // see if we have created this portal before
884 for ( i = 0 ; i < numInterAreaPortals ; i++ ) {
885 iap = &interAreaPortals[i];
887 if ( side == iap->side &&
888 ( ( p->nodes[0]->area == iap->area0 && p->nodes[1]->area == iap->area1 )
889 || ( p->nodes[1]->area == iap->area0 && p->nodes[0]->area == iap->area1 ) ) ) {
894 if ( i != numInterAreaPortals ) {
895 continue; // already emited
898 iap = &interAreaPortals[numInterAreaPortals];
899 numInterAreaPortals++;
900 if ( side->planenum == p->onnode->planenum ) {
901 iap->area0 = p->nodes[0]->area;
902 iap->area1 = p->nodes[1]->area;
904 iap->area0 = p->nodes[1]->area;
905 iap->area1 = p->nodes[0]->area;
920 Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL
921 Sets e->areas.numAreas
924 void FloodAreas( uEntity_t *e ) {
925 common->Printf ("--- FloodAreas ---\n");
927 // set all areas to -1
928 ClearAreas_r( e->tree->headnode );
930 // flood fill from non-opaque areas
932 FindAreas_r( e->tree->headnode );
934 common->Printf ("%5i areas\n", c_areas);
935 e->numAreas = c_areas;
937 // make sure we got all of them
938 CheckAreas_r( e->tree->headnode );
940 // identify all portals between areas if this is the world
941 if ( e == &dmapGlobals.uEntities[0] ) {
942 numInterAreaPortals = 0;
943 FindInterAreaPortals_r( e->tree->headnode );
948 ======================================================
952 ======================================================
955 static int c_outside;
959 void FillOutside_r (node_t *node)
961 if (node->planenum != PLANENUM_LEAF)
963 FillOutside_r (node->children[0]);
964 FillOutside_r (node->children[1]);
968 // anything not reachable by an entity
969 // can be filled away
970 if (!node->occupied) {
971 if ( !node->opaque ) {
987 Fill (set node->opaque = true) all nodes that can't be reached by entities
990 void FillOutside( uEntity_t *e ) {
994 common->Printf ("--- FillOutside ---\n");
995 FillOutside_r( e->tree->headnode );
996 common->Printf ("%5i solid leafs\n", c_solid);
997 common->Printf ("%5i leafs filled\n", c_outside);
998 common->Printf ("%5i inside leafs\n", c_inside);