]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/portals.c
set eol-style
[xonotic/netradiant.git] / tools / quake3 / q3map2 / portals.c
1 /*
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
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.
11
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.
16
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
20
21 ----------------------------------------------------------------------------------
22
23 This code has been altered significantly from its original form, to support
24 several games based on the Quake III Arena engine, in the form of "Q3Map2."
25
26 ------------------------------------------------------------------------------- */
27
28
29
30 /* marker */
31 #define PORTALS_C
32
33
34
35 /* dependencies */
36 #include "q3map2.h"
37
38
39
40 /* ydnar: to fix broken portal windings */
41 extern qboolean FixWinding( winding_t *w );
42
43
44 int             c_active_portals;
45 int             c_peak_portals;
46 int             c_boundary;
47 int             c_boundary_sides;
48
49 /*
50 ===========
51 AllocPortal
52 ===========
53 */
54 portal_t *AllocPortal (void)
55 {
56         portal_t        *p;
57         
58         if (numthreads == 1)
59                 c_active_portals++;
60         if (c_active_portals > c_peak_portals)
61                 c_peak_portals = c_active_portals;
62         
63         p = safe_malloc (sizeof(portal_t));
64         memset (p, 0, sizeof(portal_t));
65         
66         return p;
67 }
68
69 void FreePortal (portal_t *p)
70 {
71         if (p->winding)
72                 FreeWinding (p->winding);
73         if (numthreads == 1)
74                 c_active_portals--;
75         free (p);
76 }
77
78
79
80 /*
81 PortalPassable
82 returns true if the portal has non-opaque leafs on both sides
83 */
84
85 qboolean PortalPassable( portal_t *p )
86 {
87         /* is this to global outside leaf? */
88         if( !p->onnode )
89                 return qfalse;
90         
91         /* this should never happen */
92         if( p->nodes[ 0 ]->planenum != PLANENUM_LEAF ||
93                 p->nodes[ 1 ]->planenum != PLANENUM_LEAF )
94                 Error( "Portal_EntityFlood: not a leaf" );
95         
96         /* ydnar: added antiportal to supress portal generation for visibility blocking */
97         if( p->compileFlags & C_ANTIPORTAL )
98                 return qfalse;
99         
100         /* both leaves on either side of the portal must be passable */
101         if( p->nodes[ 0 ]->opaque == qfalse && p->nodes[ 1 ]->opaque == qfalse )
102                 return qtrue;
103         
104         /* otherwise this isn't a passable portal */
105         return qfalse;
106 }
107
108
109
110
111 int             c_tinyportals;
112 int             c_badportals;   /* ydnar */
113
114 /*
115 =============
116 AddPortalToNodes
117 =============
118 */
119 void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)
120 {
121         if (p->nodes[0] || p->nodes[1])
122                 Error ("AddPortalToNode: allready included");
123
124         p->nodes[0] = front;
125         p->next[0] = front->portals;
126         front->portals = p;
127         
128         p->nodes[1] = back;
129         p->next[1] = back->portals;
130         back->portals = p;
131 }
132
133
134 /*
135 =============
136 RemovePortalFromNode
137 =============
138 */
139 void RemovePortalFromNode (portal_t *portal, node_t *l)
140 {
141         portal_t        **pp, *t;
142         
143 // remove reference to the current portal
144         pp = &l->portals;
145         while (1)
146         {
147                 t = *pp;
148                 if (!t)
149                         Error ("RemovePortalFromNode: portal not in leaf");     
150
151                 if ( t == portal )
152                         break;
153
154                 if (t->nodes[0] == l)
155                         pp = &t->next[0];
156                 else if (t->nodes[1] == l)
157                         pp = &t->next[1];
158                 else
159                         Error ("RemovePortalFromNode: portal not bounding leaf");
160         }
161         
162         if (portal->nodes[0] == l)
163         {
164                 *pp = portal->next[0];
165                 portal->nodes[0] = NULL;
166         }
167         else if (portal->nodes[1] == l)
168         {
169                 *pp = portal->next[1];  
170                 portal->nodes[1] = NULL;
171         }
172 }
173
174 //============================================================================
175
176 void PrintPortal (portal_t *p)
177 {
178         int                     i;
179         winding_t       *w;
180         
181         w = p->winding;
182         for (i=0 ; i<w->numpoints ; i++)
183                 Sys_Printf ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0]
184                 , w->p[i][1], w->p[i][2]);
185 }
186
187 /*
188 ================
189 MakeHeadnodePortals
190
191 The created portals will face the global outside_node
192 ================
193 */
194 #define SIDESPACE       8
195 void MakeHeadnodePortals (tree_t *tree)
196 {
197         vec3_t          bounds[2];
198         int                     i, j, n;
199         portal_t        *p, *portals[6];
200         plane_t         bplanes[6], *pl;
201         node_t *node;
202
203         node = tree->headnode;
204
205 // pad with some space so there will never be null volume leafs
206         for (i=0 ; i<3 ; i++)
207         {
208                 bounds[0][i] = tree->mins[i] - SIDESPACE;
209                 bounds[1][i] = tree->maxs[i] + SIDESPACE;
210                 if ( bounds[0][i] >= bounds[1][i] ) {
211                         Error( "Backwards tree volume" );
212                 }
213         }
214         
215         tree->outside_node.planenum = PLANENUM_LEAF;
216         tree->outside_node.brushlist = NULL;
217         tree->outside_node.portals = NULL;
218         tree->outside_node.opaque = qfalse;
219
220         for (i=0 ; i<3 ; i++)
221                 for (j=0 ; j<2 ; j++)
222                 {
223                         n = j*3 + i;
224
225                         p = AllocPortal ();
226                         portals[n] = p;
227                         
228                         pl = &bplanes[n];
229                         memset (pl, 0, sizeof(*pl));
230                         if (j)
231                         {
232                                 pl->normal[i] = -1;
233                                 pl->dist = -bounds[j][i];
234                         }
235                         else
236                         {
237                                 pl->normal[i] = 1;
238                                 pl->dist = bounds[j][i];
239                         }
240                         p->plane = *pl;
241                         p->winding = BaseWindingForPlane (pl->normal, pl->dist);
242                         AddPortalToNodes (p, node, &tree->outside_node);
243                 }
244                 
245 // clip the basewindings by all the other planes
246         for (i=0 ; i<6 ; i++)
247         {
248                 for (j=0 ; j<6 ; j++)
249                 {
250                         if (j == i)
251                                 continue;
252                         ChopWindingInPlace (&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);
253                 }
254         }
255 }
256
257 //===================================================
258
259
260 /*
261 ================
262 BaseWindingForNode
263 ================
264 */
265 #define BASE_WINDING_EPSILON    0.001
266 #define SPLIT_WINDING_EPSILON   0.001
267
268 winding_t       *BaseWindingForNode (node_t *node)
269 {
270         winding_t       *w;
271         node_t          *n;
272         plane_t         *plane;
273         vec3_t          normal;
274         vec_t           dist;
275
276         w = BaseWindingForPlane (mapplanes[node->planenum].normal
277                 , mapplanes[node->planenum].dist);
278
279         // clip by all the parents
280         for (n=node->parent ; n && w ; )
281         {
282                 plane = &mapplanes[n->planenum];
283
284                 if (n->children[0] == node)
285                 {       // take front
286                         ChopWindingInPlace (&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);
287                 }
288                 else
289                 {       // take back
290                         VectorSubtract (vec3_origin, plane->normal, normal);
291                         dist = -plane->dist;
292                         ChopWindingInPlace (&w, normal, dist, BASE_WINDING_EPSILON);
293                 }
294                 node = n;
295                 n = n->parent;
296         }
297
298         return w;
299 }
300
301 //============================================================
302
303 /*
304 ==================
305 MakeNodePortal
306
307 create the new portal by taking the full plane winding for the cutting plane
308 and clipping it by all of parents of this node
309 ==================
310 */
311 void MakeNodePortal (node_t *node)
312 {
313         portal_t        *new_portal, *p;
314         winding_t       *w;
315         vec3_t          normal;
316         float           dist;
317         int                     side;
318
319         w = BaseWindingForNode (node);
320
321         // clip the portal by all the other portals in the node
322         for (p = node->portals ; p && w; p = p->next[side])     
323         {
324                 if (p->nodes[0] == node)
325                 {
326                         side = 0;
327                         VectorCopy (p->plane.normal, normal);
328                         dist = p->plane.dist;
329                 }
330                 else if (p->nodes[1] == node)
331                 {
332                         side = 1;
333                         VectorSubtract (vec3_origin, p->plane.normal, normal);
334                         dist = -p->plane.dist;
335                 }
336                 else
337                         Error ("CutNodePortals_r: mislinked portal");
338
339                 ChopWindingInPlace (&w, normal, dist, CLIP_EPSILON);
340         }
341
342         if (!w)
343         {
344                 return;
345         }
346         
347         
348         /* ydnar: adding this here to fix degenerate windings */
349         #if 0
350         if( FixWinding( w ) == qfalse )
351         {
352                 c_badportals++;
353                 FreeWinding( w );
354                 return;
355         }
356         #endif
357         
358         if (WindingIsTiny (w))
359         {
360                 c_tinyportals++;
361                 FreeWinding (w);
362                 return;
363         }
364
365         new_portal = AllocPortal ();
366         new_portal->plane = mapplanes[node->planenum];
367         new_portal->onnode = node;
368         new_portal->winding = w;
369         new_portal->compileFlags = node->compileFlags;
370         AddPortalToNodes (new_portal, node->children[0], node->children[1]);
371 }
372
373
374 /*
375 ==============
376 SplitNodePortals
377
378 Move or split the portals that bound node so that the node's
379 children have portals instead of node.
380 ==============
381 */
382 void SplitNodePortals (node_t *node)
383 {
384         portal_t        *p, *next_portal, *new_portal;
385         node_t          *f, *b, *other_node;
386         int                     side;
387         plane_t         *plane;
388         winding_t       *frontwinding, *backwinding;
389
390         plane = &mapplanes[node->planenum];
391         f = node->children[0];
392         b = node->children[1];
393
394         for (p = node->portals ; p ; p = next_portal)   
395         {
396                 if (p->nodes[0] == node)
397                         side = 0;
398                 else if (p->nodes[1] == node)
399                         side = 1;
400                 else
401                         Error ("SplitNodePortals: mislinked portal");
402                 next_portal = p->next[side];
403
404                 other_node = p->nodes[!side];
405                 RemovePortalFromNode (p, p->nodes[0]);
406                 RemovePortalFromNode (p, p->nodes[1]);
407
408 //
409 // cut the portal into two portals, one on each side of the cut plane
410 //
411                 ClipWindingEpsilon (p->winding, plane->normal, plane->dist,
412                         SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
413
414                 if (frontwinding && WindingIsTiny(frontwinding))
415                 {
416                         if (!f->tinyportals)
417                                 VectorCopy(frontwinding->p[0], f->referencepoint);
418                         f->tinyportals++;
419                         if (!other_node->tinyportals)
420                                 VectorCopy(frontwinding->p[0], other_node->referencepoint);
421                         other_node->tinyportals++;
422
423                         FreeWinding (frontwinding);
424                         frontwinding = NULL;
425                         c_tinyportals++;
426                 }
427
428                 if (backwinding && WindingIsTiny(backwinding))
429                 {
430                         if (!b->tinyportals)
431                                 VectorCopy(backwinding->p[0], b->referencepoint);
432                         b->tinyportals++;
433                         if (!other_node->tinyportals)
434                                 VectorCopy(backwinding->p[0], other_node->referencepoint);
435                         other_node->tinyportals++;
436
437                         FreeWinding (backwinding);
438                         backwinding = NULL;
439                         c_tinyportals++;
440                 }
441
442                 if (!frontwinding && !backwinding)
443                 {       // tiny windings on both sides
444                         continue;
445                 }
446
447                 if (!frontwinding)
448                 {
449                         FreeWinding (backwinding);
450                         if (side == 0)
451                                 AddPortalToNodes (p, b, other_node);
452                         else
453                                 AddPortalToNodes (p, other_node, b);
454                         continue;
455                 }
456                 if (!backwinding)
457                 {
458                         FreeWinding (frontwinding);
459                         if (side == 0)
460                                 AddPortalToNodes (p, f, other_node);
461                         else
462                                 AddPortalToNodes (p, other_node, f);
463                         continue;
464                 }
465                 
466         // the winding is split
467                 new_portal = AllocPortal ();
468                 *new_portal = *p;
469                 new_portal->winding = backwinding;
470                 FreeWinding (p->winding);
471                 p->winding = frontwinding;
472
473                 if (side == 0)
474                 {
475                         AddPortalToNodes (p, f, other_node);
476                         AddPortalToNodes (new_portal, b, other_node);
477                 }
478                 else
479                 {
480                         AddPortalToNodes (p, other_node, f);
481                         AddPortalToNodes (new_portal, other_node, b);
482                 }
483         }
484
485         node->portals = NULL;
486 }
487
488
489 /*
490 ================
491 CalcNodeBounds
492 ================
493 */
494 void CalcNodeBounds (node_t *node)
495 {
496         portal_t        *p;
497         int                     s;
498         int                     i;
499
500         // calc mins/maxs for both leafs and nodes
501         ClearBounds (node->mins, node->maxs);
502         for (p = node->portals ; p ; p = p->next[s])    
503         {
504                 s = (p->nodes[1] == node);
505                 for (i=0 ; i<p->winding->numpoints ; i++)
506                         AddPointToBounds (p->winding->p[i], node->mins, node->maxs);
507         }
508 }
509
510 /*
511 ==================
512 MakeTreePortals_r
513 ==================
514 */
515 void MakeTreePortals_r (node_t *node)
516 {
517         int             i;
518
519         CalcNodeBounds (node);
520         if (node->mins[0] >= node->maxs[0])
521         {
522                 Sys_Printf ("WARNING: node without a volume\n");
523                 Sys_Printf("node has %d tiny portals\n", node->tinyportals);
524                 Sys_Printf("node reference point %1.2f %1.2f %1.2f\n", node->referencepoint[0],
525                                                                                                                         node->referencepoint[1],
526                                                                                                                         node->referencepoint[2]);
527         }
528
529         for (i=0 ; i<3 ; i++)
530         {
531                 if (node->mins[i] < MIN_WORLD_COORD || node->maxs[i] > MAX_WORLD_COORD)
532                 {
533       if(node->portals && node->portals->winding)
534         xml_Winding("WARNING: Node With Unbounded Volume", node->portals->winding->p, node->portals->winding->numpoints, qfalse);
535
536                         break;
537                 }
538         }
539         if (node->planenum == PLANENUM_LEAF)
540                 return;
541
542         MakeNodePortal (node);
543         SplitNodePortals (node);
544
545         MakeTreePortals_r (node->children[0]);
546         MakeTreePortals_r (node->children[1]);
547 }
548
549 /*
550 ==================
551 MakeTreePortals
552 ==================
553 */
554 void MakeTreePortals (tree_t *tree)
555 {
556         Sys_FPrintf (SYS_VRB, "--- MakeTreePortals ---\n");
557         MakeHeadnodePortals (tree);
558         MakeTreePortals_r (tree->headnode);
559         Sys_FPrintf( SYS_VRB, "%9d tiny portals\n", c_tinyportals );
560         Sys_FPrintf( SYS_VRB, "%9d bad portals\n", c_badportals );      /* ydnar */
561 }
562
563 /*
564 =========================================================
565
566 FLOOD ENTITIES
567
568 =========================================================
569 */
570
571 int             c_floodedleafs;
572
573 /*
574 =============
575 FloodPortals_r
576 =============
577 */
578
579 void FloodPortals_r( node_t *node, int dist, qboolean skybox )
580 {
581         int                     s;
582         portal_t        *p;
583         
584         
585         if( skybox )
586                 node->skybox = skybox;
587         
588         if( node->occupied || node->opaque )
589                 return;
590         
591         c_floodedleafs++;
592         node->occupied = dist;
593         
594         for( p = node->portals; p; p = p->next[ s ] )
595         {
596                 s = (p->nodes[ 1 ] == node);
597                 FloodPortals_r( p->nodes[ !s ], dist + 1, skybox );
598         }
599 }
600
601
602
603 /*
604 =============
605 PlaceOccupant
606 =============
607 */
608
609 qboolean PlaceOccupant( node_t *headnode, vec3_t origin, entity_t *occupant, qboolean skybox )
610 {
611         vec_t   d;
612         node_t  *node;
613         plane_t *plane;
614         
615         
616         // find the leaf to start in
617         node = headnode;
618         while( node->planenum != PLANENUM_LEAF )
619         {
620                 plane = &mapplanes[ node->planenum ];
621                 d = DotProduct( origin, plane->normal ) - plane->dist;
622                 if( d >= 0 )
623                         node = node->children[ 0 ];
624                 else
625                         node = node->children[ 1 ];
626         }
627         
628         if( node->opaque )
629                 return qfalse;
630         node->occupant = occupant;
631         node->skybox = skybox;
632         
633         FloodPortals_r( node, 1, skybox );
634         
635         return qtrue;
636 }
637
638 /*
639 =============
640 FloodEntities
641
642 Marks all nodes that can be reached by entites
643 =============
644 */
645
646 qboolean FloodEntities( tree_t *tree )
647 {
648         int                     i, s;
649         vec3_t          origin, offset, scale, angles;
650         qboolean        r, inside, tripped, skybox;
651         node_t          *headnode;
652         entity_t        *e;
653         const char      *value;
654         
655         
656         headnode = tree->headnode;
657         Sys_FPrintf( SYS_VRB,"--- FloodEntities ---\n" );
658         inside = qfalse;
659         tree->outside_node.occupied = 0;
660         
661         tripped = qfalse;
662         c_floodedleafs = 0;
663         for( i = 1; i < numEntities; i++ )
664         {
665                 /* get entity */
666                 e = &entities[ i ];
667                 
668                 /* get origin */
669                 GetVectorForKey( e, "origin", origin );
670                 if( VectorCompare( origin, vec3_origin ) ) 
671                         continue;
672                 
673                 /* handle skybox entities */
674                 value = ValueForKey( e, "classname" );
675                 if( !Q_stricmp( value, "_skybox" ) )
676                 {
677                         skybox = qtrue;
678                         skyboxPresent = qtrue;
679                         
680                         /* invert origin */
681                         VectorScale( origin, -1.0f, offset );
682                         
683                         /* get scale */
684                         VectorSet( scale, 64.0f, 64.0f, 64.0f );
685                         value = ValueForKey( e, "_scale" );
686                         if( value[ 0 ] != '\0' )
687                         {
688                                 s = sscanf( value, "%f %f %f", &scale[ 0 ], &scale[ 1 ], &scale[ 2 ] );
689                                 if( s == 1 )
690                                 {
691                                         scale[ 1 ] = scale[ 0 ];
692                                         scale[ 2 ] = scale[ 0 ];
693                                 }
694                         }
695                         
696                         /* get "angle" (yaw) or "angles" (pitch yaw roll) */
697                         VectorClear( angles );
698                         angles[ 2 ] = FloatForKey( e, "angle" );
699                         value = ValueForKey( e, "angles" );
700                         if( value[ 0 ] != '\0' )
701                                 sscanf( value, "%f %f %f", &angles[ 1 ], &angles[ 2 ], &angles[ 0 ] );
702                         
703                         /* set transform matrix (thanks spog) */
704                         m4x4_identity( skyboxTransform );
705                         m4x4_pivoted_transform_by_vec3( skyboxTransform, offset, angles, eXYZ, scale, origin );
706                 }
707                 else
708                         skybox = qfalse;
709                 
710                 /* nudge off floor */
711                 origin[ 2 ] += 1;
712                 
713                 /* debugging code */
714                 //%     if( i == 1 )
715                 //%             origin[ 2 ] += 4096;
716                 
717                 /* find leaf */
718                 r = PlaceOccupant( headnode, origin, e, skybox );
719                 if( r )
720                         inside = qtrue;
721                 if( (!r || tree->outside_node.occupied) && !tripped )
722                 {
723                         xml_Select( "Entity leaked", e->mapEntityNum, 0, qfalse );
724                         tripped = qtrue;
725                 }
726         }
727         
728         Sys_FPrintf( SYS_VRB, "%9d flooded leafs\n", c_floodedleafs );
729         
730         if( !inside )
731                 Sys_FPrintf( SYS_VRB, "no entities in open -- no filling\n" );
732         else if( tree->outside_node.occupied )
733                 Sys_FPrintf( SYS_VRB, "entity reached from outside -- no filling\n" );
734         
735         return (qboolean) (inside && !tree->outside_node.occupied);
736 }
737
738 /*
739 =========================================================
740
741 FLOOD AREAS
742
743 =========================================================
744 */
745
746 int             c_areas;
747
748
749
750 /*
751 FloodAreas_r()
752 floods through leaf portals to tag leafs with an area
753 */
754
755 void FloodAreas_r( node_t *node )
756 {
757         int                     s;
758         portal_t        *p;
759         brush_t         *b;
760         
761         
762         if( node->areaportal )
763         {
764                 if( node->area == -1 )
765                         node->area = c_areas;
766                 
767                 /* this node is part of an area portal brush */
768                 b = node->brushlist->original;
769
770                 /* if the current area has already touched this portal, we are done */
771                 if( b->portalareas[ 0 ] == c_areas || b->portalareas[ 1 ] == c_areas )
772                         return;
773                 
774                 // note the current area as bounding the portal
775                 if( b->portalareas[ 1 ] != -1 )
776                 {
777                         Sys_Printf( "WARNING: areaportal brush %i touches > 2 areas\n", b->brushNum );
778                         return;
779                 }
780                 if( b->portalareas[ 0 ] != -1 )
781                         b->portalareas[ 1 ] = c_areas;
782                 else
783                         b->portalareas[ 0 ] = c_areas;
784                 
785                 return;
786         }
787
788         if( node->area != -1 )
789                 return; 
790         if( node->cluster == -1 )
791                 return;
792
793         node->area = c_areas;
794         
795         /* ydnar: skybox nodes set the skybox area */
796         if( node->skybox )
797                 skyboxArea = c_areas;
798         
799         for( p = node->portals; p; p = p->next[ s ] )
800         {
801                 s = (p->nodes[1] == node);
802                 
803                 /* ydnar: allow areaportal portals to block area flow */
804                 if( p->compileFlags & C_AREAPORTAL )
805                         continue;
806                 
807                 if( !PortalPassable( p ) )
808                         continue;
809                 
810                 FloodAreas_r( p->nodes[ !s ] );
811         }
812 }
813
814 /*
815 =============
816 FindAreas_r
817
818 Just decend the tree, and for each node that hasn't had an
819 area set, flood fill out from there
820 =============
821 */
822 void FindAreas_r( node_t *node )
823 {
824         if( node->planenum != PLANENUM_LEAF )
825         {
826                 FindAreas_r( node->children[ 0 ] );
827                 FindAreas_r( node->children[ 1 ] );
828                 return;
829         }
830         
831         if( node->opaque || node->areaportal || node->area != -1 )
832                 return;
833         
834         FloodAreas_r( node );
835         c_areas++;
836 }
837
838 /*
839 =============
840 CheckAreas_r
841 =============
842 */
843 void CheckAreas_r (node_t *node)
844 {
845         brush_t *b;
846
847         if (node->planenum != PLANENUM_LEAF)
848         {
849                 CheckAreas_r (node->children[0]);
850                 CheckAreas_r (node->children[1]);
851                 return;
852         }
853
854         if (node->opaque)
855                 return;
856
857         if (node->cluster != -1)
858                 if (node->area == -1)
859                         Sys_Printf("WARNING: cluster %d has area set to -1\n", node->cluster);
860         if (node->areaportal)
861         {
862                 b = node->brushlist->original;
863
864                 // check if the areaportal touches two areas
865                 if (b->portalareas[0] == -1 || b->portalareas[1] == -1)
866                         Sys_Printf ("WARNING: areaportal brush %i doesn't touch two areas\n", b->brushNum);
867         }
868 }
869
870
871
872 /*
873 FloodSkyboxArea_r() - ydnar
874 sets all nodes with the skybox area to skybox
875 */
876
877 void FloodSkyboxArea_r( node_t *node )
878 {
879         if( skyboxArea < 0 )
880                 return;
881         
882         if( node->planenum != PLANENUM_LEAF )
883         {
884                 FloodSkyboxArea_r( node->children[ 0 ] );
885                 FloodSkyboxArea_r( node->children[ 1 ] );
886                 return;
887         }
888         
889         if( node->opaque || node->area != skyboxArea )
890                 return;
891         
892         node->skybox = qtrue;
893 }
894
895
896
897 /*
898 FloodAreas()
899 mark each leaf with an area, bounded by C_AREAPORTAL
900 */
901
902 void FloodAreas( tree_t *tree )
903 {
904         Sys_FPrintf( SYS_VRB,"--- FloodAreas ---\n" );
905         FindAreas_r( tree->headnode );
906         
907         /* ydnar: flood all skybox nodes */
908         FloodSkyboxArea_r( tree->headnode );
909         
910         /* check for areaportal brushes that don't touch two areas */
911         /* ydnar: fix this rather than just silence the warnings */
912         //%     CheckAreas_r( tree->headnode );
913
914         Sys_FPrintf( SYS_VRB, "%9d areas\n", c_areas );
915 }
916
917
918
919 //======================================================
920
921 int             c_outside;
922 int             c_inside;
923 int             c_solid;
924
925 void FillOutside_r (node_t *node)
926 {
927         if (node->planenum != PLANENUM_LEAF)
928         {
929                 FillOutside_r (node->children[0]);
930                 FillOutside_r (node->children[1]);
931                 return;
932         }
933
934         // anything not reachable by an entity
935         // can be filled away
936         if (!node->occupied) {
937                 if ( !node->opaque ) {
938                         c_outside++;
939                         node->opaque = qtrue;
940                 } else {
941                         c_solid++;
942                 }
943         } else {
944                 c_inside++;
945         }
946
947 }
948
949 /*
950 =============
951 FillOutside
952
953 Fill all nodes that can't be reached by entities
954 =============
955 */
956 void FillOutside (node_t *headnode)
957 {
958         c_outside = 0;
959         c_inside = 0;
960         c_solid = 0;
961         Sys_FPrintf( SYS_VRB,"--- FillOutside ---\n" );
962         FillOutside_r( headnode );
963         Sys_FPrintf( SYS_VRB,"%9d solid leafs\n", c_solid );
964         Sys_Printf( "%9d leafs filled\n", c_outside );
965         Sys_FPrintf( SYS_VRB, "%9d inside leafs\n", c_inside );
966 }
967
968
969 //==============================================================
970