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 ===========================================================================
36 portal_t *AllocPortal (void)
42 if (c_active_portals > c_peak_portals)
43 c_peak_portals = c_active_portals;
45 p = malloc (sizeof(portal_t));
46 memset (p, 0, sizeof(portal_t));
51 void FreePortal (portal_t *p)
54 FreeWinding (p->winding);
60 //==============================================================
66 Returns the single content bit of the
67 strongest visible content present
70 int VisibleContents (int contents)
74 for (i=1 ; i<=LAST_VISIBLE_CONTENTS ; i<<=1)
87 int ClusterContents (node_t *node)
91 if (node->planenum == PLANENUM_LEAF)
92 return node->contents;
94 c1 = ClusterContents(node->children[0]);
95 c2 = ClusterContents(node->children[1]);
98 // a cluster may include some solid detail areas, but
100 if ( ! (c1&CONTENTS_SOLID) || ! (c2&CONTENTS_SOLID) )
101 c &= ~CONTENTS_SOLID;
109 Returns true if the portal is empty or translucent, allowing
110 the PVS calculation to see through it.
111 The nodes on either side of the portal may actually be clusters,
112 not leafs, so all contents should be ored together
115 qboolean Portal_VisFlood (portal_t *p)
120 return false; // to global outsideleaf
122 c1 = ClusterContents(p->nodes[0]);
123 c2 = ClusterContents(p->nodes[1]);
125 if (!VisibleContents (c1^c2))
128 if (c1 & (CONTENTS_TRANSLUCENT|CONTENTS_DETAIL))
130 if (c2 & (CONTENTS_TRANSLUCENT|CONTENTS_DETAIL))
133 if ( (c1|c2) & CONTENTS_SOLID )
134 return false; // can't see through solid
137 return true; // identical on both sides
139 if (!VisibleContents (c1^c2))
149 The entity flood determines which areas are
150 "outside" on the map, which are then filled in.
151 Flowing from side s to side !s
154 qboolean Portal_EntityFlood (portal_t *p, int s)
156 if (p->nodes[0]->planenum != PLANENUM_LEAF
157 || p->nodes[1]->planenum != PLANENUM_LEAF)
158 Error ("Portal_EntityFlood: not a leaf");
160 // can never cross to a solid
161 if ( (p->nodes[0]->contents & CONTENTS_SOLID)
162 || (p->nodes[1]->contents & CONTENTS_SOLID) )
165 // can flood through everything else
170 //=============================================================================
179 void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)
181 if (p->nodes[0] || p->nodes[1])
182 Error ("AddPortalToNode: allready included");
185 p->next[0] = front->portals;
189 p->next[1] = back->portals;
199 void RemovePortalFromNode (portal_t *portal, node_t *l)
203 // remove reference to the current portal
209 Error ("RemovePortalFromNode: portal not in leaf");
214 if (t->nodes[0] == l)
216 else if (t->nodes[1] == l)
219 Error ("RemovePortalFromNode: portal not bounding leaf");
222 if (portal->nodes[0] == l)
224 *pp = portal->next[0];
225 portal->nodes[0] = NULL;
227 else if (portal->nodes[1] == l)
229 *pp = portal->next[1];
230 portal->nodes[1] = NULL;
234 //============================================================================
236 void PrintPortal (portal_t *p)
242 for (i=0 ; i<w->numpoints ; i++)
243 printf ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0]
244 , w->p[i][1], w->p[i][2]);
251 The created portals will face the global outside_node
255 void MakeHeadnodePortals (tree_t *tree)
259 portal_t *p, *portals[6];
260 plane_t bplanes[6], *pl;
263 node = tree->headnode;
265 // pad with some space so there will never be null volume leafs
266 for (i=0 ; i<3 ; i++)
268 bounds[0][i] = tree->mins[i] - SIDESPACE;
269 bounds[1][i] = tree->maxs[i] + SIDESPACE;
272 tree->outside_node.planenum = PLANENUM_LEAF;
273 tree->outside_node.brushlist = NULL;
274 tree->outside_node.portals = NULL;
275 tree->outside_node.contents = 0;
277 for (i=0 ; i<3 ; i++)
278 for (j=0 ; j<2 ; j++)
286 memset (pl, 0, sizeof(*pl));
290 pl->dist = -bounds[j][i];
295 pl->dist = bounds[j][i];
298 p->winding = BaseWindingForPlane (pl->normal, pl->dist);
299 AddPortalToNodes (p, node, &tree->outside_node);
302 // clip the basewindings by all the other planes
303 for (i=0 ; i<6 ; i++)
305 for (j=0 ; j<6 ; j++)
309 ChopWindingInPlace (&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);
314 //===================================================
322 #define BASE_WINDING_EPSILON 0.001
323 #define SPLIT_WINDING_EPSILON 0.001
325 winding_t *BaseWindingForNode (node_t *node)
333 w = BaseWindingForPlane (mapplanes[node->planenum].normal
334 , mapplanes[node->planenum].dist);
336 // clip by all the parents
337 for (n=node->parent ; n && w ; )
339 plane = &mapplanes[n->planenum];
341 if (n->children[0] == node)
343 ChopWindingInPlace (&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);
347 VectorSubtract (vec3_origin, plane->normal, normal);
349 ChopWindingInPlace (&w, normal, dist, BASE_WINDING_EPSILON);
358 //============================================================
360 qboolean WindingIsTiny (winding_t *w);
366 create the new portal by taking the full plane winding for the cutting plane
367 and clipping it by all of parents of this node
370 void MakeNodePortal (node_t *node)
372 portal_t *new_portal, *p;
378 w = BaseWindingForNode (node);
380 // clip the portal by all the other portals in the node
381 for (p = node->portals ; p && w; p = p->next[side])
383 if (p->nodes[0] == node)
386 VectorCopy (p->plane.normal, normal);
387 dist = p->plane.dist;
389 else if (p->nodes[1] == node)
392 VectorSubtract (vec3_origin, p->plane.normal, normal);
393 dist = -p->plane.dist;
396 Error ("CutNodePortals_r: mislinked portal");
398 ChopWindingInPlace (&w, normal, dist, 0.1);
406 if (WindingIsTiny (w))
414 new_portal = AllocPortal ();
415 new_portal->plane = mapplanes[node->planenum];
416 new_portal->onnode = node;
417 new_portal->winding = w;
418 AddPortalToNodes (new_portal, node->children[0], node->children[1]);
426 Move or split the portals that bound node so that the node's
427 children have portals instead of node.
430 void SplitNodePortals (node_t *node)
432 portal_t *p, *next_portal, *new_portal;
433 node_t *f, *b, *other_node;
436 winding_t *frontwinding, *backwinding;
438 plane = &mapplanes[node->planenum];
439 f = node->children[0];
440 b = node->children[1];
442 for (p = node->portals ; p ; p = next_portal)
444 if (p->nodes[0] == node)
446 else if (p->nodes[1] == node)
449 Error ("CutNodePortals_r: mislinked portal");
450 next_portal = p->next[side];
452 other_node = p->nodes[!side];
453 RemovePortalFromNode (p, p->nodes[0]);
454 RemovePortalFromNode (p, p->nodes[1]);
457 // cut the portal into two portals, one on each side of the cut plane
459 ClipWindingEpsilon (p->winding, plane->normal, plane->dist,
460 SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
462 if (frontwinding && WindingIsTiny(frontwinding))
464 FreeWinding (frontwinding);
469 if (backwinding && WindingIsTiny(backwinding))
471 FreeWinding (backwinding);
476 if (!frontwinding && !backwinding)
477 { // tiny windings on both sides
483 FreeWinding (backwinding);
485 AddPortalToNodes (p, b, other_node);
487 AddPortalToNodes (p, other_node, b);
492 FreeWinding (frontwinding);
494 AddPortalToNodes (p, f, other_node);
496 AddPortalToNodes (p, other_node, f);
500 // the winding is split
501 new_portal = AllocPortal ();
503 new_portal->winding = backwinding;
504 FreeWinding (p->winding);
505 p->winding = frontwinding;
509 AddPortalToNodes (p, f, other_node);
510 AddPortalToNodes (new_portal, b, other_node);
514 AddPortalToNodes (p, other_node, f);
515 AddPortalToNodes (new_portal, other_node, b);
519 node->portals = NULL;
528 void CalcNodeBounds (node_t *node)
534 // calc mins/maxs for both leafs and nodes
535 ClearBounds (node->mins, node->maxs);
536 for (p = node->portals ; p ; p = p->next[s])
538 s = (p->nodes[1] == node);
539 for (i=0 ; i<p->winding->numpoints ; i++)
540 AddPointToBounds (p->winding->p[i], node->mins, node->maxs);
550 void MakeTreePortals_r (node_t *node)
554 CalcNodeBounds (node);
555 if (node->mins[0] >= node->maxs[0])
557 printf ("WARNING: node without a volume\n");
560 for (i=0 ; i<3 ; i++)
562 if (node->mins[i] < -8000 || node->maxs[i] > 8000)
564 printf ("WARNING: node with unbounded volume\n");
568 if (node->planenum == PLANENUM_LEAF)
571 MakeNodePortal (node);
572 SplitNodePortals (node);
574 MakeTreePortals_r (node->children[0]);
575 MakeTreePortals_r (node->children[1]);
583 void MakeTreePortals (tree_t *tree)
585 MakeHeadnodePortals (tree);
586 MakeTreePortals_r (tree->headnode);
590 =========================================================
594 =========================================================
602 void FloodPortals_r (node_t *node, int dist)
607 node->occupied = dist;
609 for (p=node->portals ; p ; p = p->next[s])
611 s = (p->nodes[1] == node);
613 if (p->nodes[!s]->occupied)
616 if (!Portal_EntityFlood (p, s))
619 FloodPortals_r (p->nodes[!s], dist+1);
628 qboolean PlaceOccupant (node_t *headnode, vec3_t origin, entity_t *occupant)
634 // find the leaf to start in
636 while (node->planenum != PLANENUM_LEAF)
638 plane = &mapplanes[node->planenum];
639 d = DotProduct (origin, plane->normal) - plane->dist;
641 node = node->children[0];
643 node = node->children[1];
646 if (node->contents == CONTENTS_SOLID)
648 node->occupant = occupant;
650 FloodPortals_r (node, 1);
659 Marks all nodes that can be reached by entites
662 qboolean FloodEntities (tree_t *tree)
670 headnode = tree->headnode;
671 qprintf ("--- FloodEntities ---\n");
673 tree->outside_node.occupied = 0;
675 for (i=1 ; i<num_entities ; i++)
677 GetVectorForKey (&entities[i], "origin", origin);
678 if (VectorCompare(origin, vec3_origin))
681 cl = ValueForKey (&entities[i], "classname");
682 origin[2] += 1; // so objects on floor are ok
684 // nudge playerstart around if needed so clipping hulls allways
685 // have a vlaid point
686 if (!strcmp (cl, "info_player_start"))
690 for (x=-16 ; x<=16 ; x += 16)
692 for (y=-16 ; y<=16 ; y += 16)
696 if (PlaceOccupant (headnode, origin, &entities[i]))
709 if (PlaceOccupant (headnode, origin, &entities[i]))
716 qprintf ("no entities in open -- no filling\n");
718 else if (tree->outside_node.occupied)
720 qprintf ("entity reached from outside -- no filling\n");
723 return (qboolean)(inside && !tree->outside_node.occupied);
727 =========================================================
731 =========================================================
741 void FloodAreas_r (node_t *node)
748 if (node->contents == CONTENTS_AREAPORTAL)
750 // this node is part of an area portal
752 e = &entities[b->original->entitynum];
754 // if the current area has allready touched this
755 // portal, we are done
756 if (e->portalareas[0] == c_areas || e->portalareas[1] == c_areas)
759 // note the current area as bounding the portal
760 if (e->portalareas[1])
762 printf ("WARNING: areaportal entity %i touches > 2 areas\n", b->original->entitynum);
765 if (e->portalareas[0])
766 e->portalareas[1] = c_areas;
768 e->portalareas[0] = c_areas;
774 return; // allready got it
775 node->area = c_areas;
777 for (p=node->portals ; p ; p = p->next[s])
779 s = (p->nodes[1] == node);
781 if (p->nodes[!s]->occupied)
784 if (!Portal_EntityFlood (p, s))
787 FloodAreas_r (p->nodes[!s]);
795 Just decend the tree, and for each node that hasn't had an
796 area set, flood fill out from there
799 void FindAreas_r (node_t *node)
801 if (node->planenum != PLANENUM_LEAF)
803 FindAreas_r (node->children[0]);
804 FindAreas_r (node->children[1]);
809 return; // allready got it
811 if (node->contents & CONTENTS_SOLID)
815 return; // not reachable by entities
817 // area portals are allways only flooded into, never
819 if (node->contents == CONTENTS_AREAPORTAL)
830 Just decend the tree, and for each node that hasn't had an
831 area set, flood fill out from there
834 void SetAreaPortalAreas_r (node_t *node)
839 if (node->planenum != PLANENUM_LEAF)
841 SetAreaPortalAreas_r (node->children[0]);
842 SetAreaPortalAreas_r (node->children[1]);
846 if (node->contents == CONTENTS_AREAPORTAL)
849 return; // allready set
852 e = &entities[b->original->entitynum];
853 node->area = e->portalareas[0];
854 if (!e->portalareas[1])
856 printf ("WARNING: areaportal entity %i doesn't touch two areas\n", b->original->entitynum);
868 void EmitAreaPortals (node_t *headnode)
874 if (c_areas > MAX_MAP_AREAS)
875 Error ("MAX_MAP_AREAS");
876 numareas = c_areas+1;
877 numareaportals = 1; // leave 0 as an error
879 for (i=1 ; i<=c_areas ; i++)
881 dareas[i].firstareaportal = numareaportals;
882 for (j=0 ; j<num_entities ; j++)
885 if (!e->areaportalnum)
887 dp = &dareaportals[numareaportals];
888 if (e->portalareas[0] == i)
890 dp->portalnum = e->areaportalnum;
891 dp->otherarea = e->portalareas[1];
894 else if (e->portalareas[1] == i)
896 dp->portalnum = e->areaportalnum;
897 dp->otherarea = e->portalareas[0];
901 dareas[i].numareaportals = numareaportals - dareas[i].firstareaportal;
904 qprintf ("%5i numareas\n", numareas);
905 qprintf ("%5i numareaportals\n", numareaportals);
912 Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL
915 void FloodAreas (tree_t *tree)
917 qprintf ("--- FloodAreas ---\n");
918 FindAreas_r (tree->headnode);
919 SetAreaPortalAreas_r (tree->headnode);
920 qprintf ("%5i areas\n", c_areas);
923 //======================================================
929 void FillOutside_r (node_t *node)
931 if (node->planenum != PLANENUM_LEAF)
933 FillOutside_r (node->children[0]);
934 FillOutside_r (node->children[1]);
938 // anything not reachable by an entity
939 // can be filled away
942 if (node->contents != CONTENTS_SOLID)
945 node->contents = CONTENTS_SOLID;
959 Fill all nodes that can't be reached by entities
962 void FillOutside (node_t *headnode)
967 qprintf ("--- FillOutside ---\n");
968 FillOutside_r (headnode);
969 qprintf ("%5i solid leafs\n", c_solid);
970 qprintf ("%5i leafs filled\n", c_outside);
971 qprintf ("%5i inside leafs\n", c_inside);
975 //==============================================================
981 Finds a brush side to use for texturing the given portal
984 void FindPortalSide (portal_t *p)
992 side_t *side, *bestside;
996 // decide which content change is strongest
997 // solid > lava > water, etc
998 viscontents = VisibleContents (p->nodes[0]->contents ^ p->nodes[1]->contents);
1002 planenum = p->onnode->planenum;
1006 for (j=0 ; j<2 ; j++)
1009 p1 = &mapplanes[p->onnode->planenum];
1010 for (bb=n->brushlist ; bb ; bb=bb->next)
1012 brush = bb->original;
1013 if ( !(brush->contents & viscontents) )
1015 for (i=0 ; i<brush->numsides ; i++)
1017 side = &brush->original_sides[i];
1020 if (side->texinfo == TEXINFO_NODE)
1021 continue; // non-visible
1022 if ((side->planenum&~1) == planenum)
1024 bestside = &brush->original_sides[i];
1027 // see how close the match is
1028 p2 = &mapplanes[side->planenum&~1];
1029 dot = DotProduct (p1->normal, p2->normal);
1041 qprintf ("WARNING: side not found for portal\n");
1043 p->sidefound = true;
1054 void MarkVisibleSides_r (node_t *node)
1059 if (node->planenum != PLANENUM_LEAF)
1061 MarkVisibleSides_r (node->children[0]);
1062 MarkVisibleSides_r (node->children[1]);
1066 // empty leafs are never boundary leafs
1067 if (!node->contents)
1070 // see if there is a visible face
1071 for (p=node->portals ; p ; p = p->next[!s])
1073 s = (p->nodes[0] == node);
1075 continue; // edge of world
1079 p->side->visible = true;
1090 void MarkVisibleSides (tree_t *tree, int startbrush, int endbrush)
1096 qprintf ("--- MarkVisibleSides ---\n");
1098 // clear all the visible flags
1099 for (i=startbrush ; i<endbrush ; i++)
1101 mb = &mapbrushes[i];
1103 numsides = mb->numsides;
1104 for (j=0 ; j<numsides ; j++)
1105 mb->original_sides[j].visible = false;
1108 // set visible flags on the sides that are used by portals
1109 MarkVisibleSides_r (tree->headnode);