]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/fog.c
set eol-style
[xonotic/netradiant.git] / tools / quake3 / q3map2 / fog.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 FOG_C
32
33
34
35 /* dependencies */
36 #include "q3map2.h"
37
38
39
40 int                     numFogFragments;
41 int                     numFogPatchFragments;
42
43
44
45 /*
46 DrawSurfToMesh()
47 converts a patch drawsurface to a mesh_t
48 */
49
50 mesh_t *DrawSurfToMesh( mapDrawSurface_t *ds )
51 {
52         mesh_t          *m;
53         
54         
55         m = safe_malloc( sizeof( *m ) );
56         m->width = ds->patchWidth;
57         m->height = ds->patchHeight;
58         m->verts = safe_malloc( sizeof(m->verts[ 0 ]) * m->width * m->height );
59         memcpy( m->verts, ds->verts, sizeof(m->verts[ 0 ]) * m->width * m->height );
60         
61         return m;
62 }
63
64
65
66 /*
67 SplitMeshByPlane()
68 chops a mesh by a plane
69 */
70
71 void SplitMeshByPlane( mesh_t *in, vec3_t normal, float dist, mesh_t **front, mesh_t **back )
72 {
73         int             w, h, split;
74         float   d[MAX_PATCH_SIZE][MAX_PATCH_SIZE];
75         bspDrawVert_t   *dv, *v1, *v2;
76         int             c_front, c_back, c_on;
77         mesh_t  *f, *b;
78         int             i;
79         float   frac;
80         int             frontAprox, backAprox;
81
82         for ( i = 0 ; i < 2 ; i++ ) {
83                 dv = in->verts;
84                 c_front = 0;
85                 c_back = 0;
86                 c_on = 0;
87                 for ( h = 0 ; h < in->height ; h++ ) {
88                         for ( w = 0 ; w < in->width ; w++, dv++ ) {
89                                 d[h][w] = DotProduct( dv->xyz, normal ) - dist;
90                                 if ( d[h][w] > ON_EPSILON ) {
91                                         c_front++;
92                                 } else if ( d[h][w] < -ON_EPSILON ) {
93                                         c_back++;
94                                 } else {
95                                         c_on++;
96                                 }
97                         }
98                 }
99
100                 *front = NULL;
101                 *back = NULL;
102
103                 if ( !c_front ) {
104                         *back = in;
105                         return;
106                 }
107                 if ( !c_back ) {
108                         *front = in;
109                         return;
110                 }
111
112                 // find a split point
113                 split = -1;
114                 for ( w = 0 ; w < in->width -1 ; w++ ) {
115                         if ( ( d[0][w] < 0 ) != ( d[0][w+1] < 0 ) ) {
116                                 if ( split == -1 ) {
117                                         split = w;
118                                         break;
119                                 }
120                         }
121                 }
122
123                 if ( split == -1 ) {
124                         if ( i == 1 ) {
125                                 Sys_FPrintf (SYS_VRB, "No crossing points in patch\n");
126                                 *front = in;
127                                 return;
128                         }
129
130                         in = TransposeMesh( in );
131                         InvertMesh( in );
132                         continue;
133                 }
134
135                 // make sure the split point stays the same for all other rows
136                 for ( h = 1 ; h < in->height ; h++ ) {
137                         for ( w = 0 ; w < in->width -1 ; w++ ) {
138                                 if ( ( d[h][w] < 0 ) != ( d[h][w+1] < 0 ) ) {
139                                         if ( w != split ) {
140                                                 Sys_Printf( "multiple crossing points for patch -- can't clip\n");
141                                                 *front = in;
142                                                 return;
143                                         }
144                                 }
145                         }
146                         if ( ( d[h][split] < 0 ) == ( d[h][split+1] < 0 ) ) {
147                                 Sys_Printf( "differing crossing points for patch -- can't clip\n");
148                                 *front = in;
149                                 return;
150                         }
151                 }
152
153                 break;
154         }
155
156
157         // create two new meshes
158         f = safe_malloc( sizeof( *f ) );
159         f->width = split + 2;
160         if ( ! (f->width & 1) ) {
161                 f->width++;
162                 frontAprox = 1;
163         } else {
164                 frontAprox = 0;
165         }
166         if ( f->width > MAX_PATCH_SIZE ) {
167                 Error( "MAX_PATCH_SIZE after split");
168         }
169         f->height = in->height;
170         f->verts = safe_malloc( sizeof(f->verts[0]) * f->width * f->height );
171
172         b = safe_malloc( sizeof( *b ) );
173         b->width = in->width - split;
174         if ( ! (b->width & 1) ) {
175                 b->width++;
176                 backAprox = 1;
177         } else {
178                 backAprox = 0;
179         }
180         if ( b->width > MAX_PATCH_SIZE ) {
181                 Error( "MAX_PATCH_SIZE after split");
182         }
183         b->height = in->height;
184         b->verts = safe_malloc( sizeof(b->verts[0]) * b->width * b->height );
185
186         if ( d[0][0] > 0 ) {
187                 *front = f;
188                 *back = b;
189         } else {
190                 *front = b;
191                 *back = f;
192         }
193
194         // distribute the points
195         for ( w = 0 ; w < in->width ; w++ ) {
196                 for ( h = 0 ; h < in->height ; h++ ) {
197                         if ( w <= split ) {
198                                 f->verts[ h * f->width + w ] = in->verts[ h * in->width + w ];
199                         } else {
200                                 b->verts[ h * b->width + w - split + backAprox ] = in->verts[ h * in->width + w ];
201                         }
202                 }
203         }
204
205         // clip the crossing line
206         for ( h = 0; h < in->height; h++ )
207         {
208                 dv = &f->verts[ h * f->width + split + 1 ];
209                 v1 = &in->verts[ h * in->width + split ];
210                 v2 = &in->verts[ h * in->width + split + 1 ];
211
212                 frac = d[h][split] / ( d[h][split] - d[h][split+1] );
213                 
214                 /* interpolate */
215                 //%     for( i = 0; i < 10; i++ )
216                 //%             dv->xyz[ i ] = v1->xyz[ i ] + frac * (v2->xyz[ i ] - v1->xyz[ i ]);
217                 //%     dv->xyz[10] = 0;        // set all 4 colors to 0 
218                 LerpDrawVertAmount( v1, v2, frac, dv );
219                 
220                 if ( frontAprox ) {
221                         f->verts[ h * f->width + split + 2 ] = *dv;
222                 }
223                 b->verts[ h * b->width ] = *dv;
224                 if ( backAprox ) {
225                         b->verts[ h * b->width + 1 ] = *dv;
226                 }
227         }
228
229         /*
230 PrintMesh( in );
231 Sys_Printf("\n");
232 PrintMesh( f );
233 Sys_Printf("\n");
234 PrintMesh( b );
235 Sys_Printf("\n");
236         */
237
238         FreeMesh( in );
239 }
240
241
242 /*
243 ChopPatchSurfaceByBrush()
244 chops a patch up by a fog brush
245 */
246
247 qboolean ChopPatchSurfaceByBrush( entity_t *e, mapDrawSurface_t *ds, brush_t *b )
248 {
249         int                     i, j;
250         side_t          *s;
251         plane_t         *plane;
252         mesh_t          *outside[MAX_BRUSH_SIDES];
253         int                     numOutside;
254         mesh_t          *m, *front, *back;
255         mapDrawSurface_t        *newds;
256
257         m = DrawSurfToMesh( ds );
258         numOutside = 0;
259         
260         // only split by the top and bottom planes to avoid
261         // some messy patch clipping issues
262         
263         for ( i = 4 ; i <= 5 ; i++ ) {
264                 s = &b->sides[ i ];
265                 plane = &mapplanes[ s->planenum ];
266
267                 SplitMeshByPlane( m, plane->normal, plane->dist, &front, &back );
268
269                 if ( !back ) {
270                         // nothing actually contained inside
271                         for ( j = 0 ; j < numOutside ; j++ ) {
272                                 FreeMesh( outside[j] );
273                         }
274                         return qfalse;
275                 }
276                 m = back;
277
278                 if ( front ) {
279                         if ( numOutside == MAX_BRUSH_SIDES ) {
280                                 Error( "MAX_BRUSH_SIDES" );
281                         }
282                         outside[ numOutside ] = front;
283                         numOutside++;
284                 }
285         }
286
287         /* all of outside fragments become seperate drawsurfs */
288         numFogPatchFragments += numOutside;
289         for( i = 0; i < numOutside; i++ )
290         {
291                 /* transpose and invert the chopped patch (fixes potential crash. fixme: why?) */
292                 outside[ i ] = TransposeMesh( outside[ i ] );
293                 InvertMesh( outside[ i ] );
294                 
295                 /* ydnar: do this the hacky right way */
296                 newds = AllocDrawSurface( SURFACE_PATCH );
297                 memcpy( newds, ds, sizeof( *ds ) );
298                 newds->patchWidth = outside[ i ]->width;
299                 newds->patchHeight = outside[ i ]->height;
300                 newds->numVerts = outside[ i ]->width * outside[ i ]->height;
301                 newds->verts = safe_malloc( newds->numVerts * sizeof( *newds->verts ) );
302                 memcpy( newds->verts, outside[ i ]->verts, newds->numVerts * sizeof( *newds->verts ) );
303                 
304                 /* free the source mesh */
305                 FreeMesh( outside[ i ] );
306         }
307         
308         /* only rejigger this patch if it was chopped */
309         //%     Sys_Printf( "Inside: %d x %d\n", m->width, m->height );
310         if( numOutside > 0 )
311         {
312                 /* transpose and invert the chopped patch (fixes potential crash. fixme: why?) */
313                 m = TransposeMesh( m );
314                 InvertMesh( m );
315                 
316                 /* replace ds with m */
317                 ds->patchWidth = m->width;
318                 ds->patchHeight = m->height;
319                 ds->numVerts = m->width * m->height;
320                 free( ds->verts );
321                 ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );
322                 memcpy( ds->verts, m->verts, ds->numVerts * sizeof( *ds->verts ) );
323         }
324         
325         /* free the source mesh and return */
326         FreeMesh( m );
327         return qtrue;
328 }
329
330
331
332 /*
333 WindingFromDrawSurf()
334 creates a winding from a surface's verts
335 */
336
337 winding_t *WindingFromDrawSurf( mapDrawSurface_t *ds )
338 {
339         winding_t       *w;
340         int                     i;
341
342         // we use the first point of the surface, maybe something more clever would be useful
343         // (actually send the whole draw surface would be cool?)
344         if( ds->numVerts >= MAX_POINTS_ON_WINDING )
345         {
346                 int max = ds->numVerts;
347                 vec3_t p[256];
348
349                 if(max > 256)
350                         max = 256;
351
352                 for ( i = 0 ; i < max ; i++ ) {
353                         VectorCopy( ds->verts[i].xyz, p[i] );
354                 }
355
356                 xml_Winding( "WindingFromDrawSurf failed: MAX_POINTS_ON_WINDING exceeded", p, max, qtrue );
357         }
358
359         w = AllocWinding( ds->numVerts );
360         w->numpoints = ds->numVerts;
361         for ( i = 0 ; i < ds->numVerts ; i++ ) {
362                 VectorCopy( ds->verts[i].xyz, w->p[i] );
363         }
364         return w;
365 }
366
367
368
369 /*
370 ChopFaceSurfaceByBrush()
371 chops up a face drawsurface by a fog brush, with a potential fragment left inside
372 */
373
374 qboolean ChopFaceSurfaceByBrush( entity_t *e, mapDrawSurface_t *ds, brush_t *b )
375 {
376         int                                     i, j;
377         side_t                          *s;
378         plane_t                         *plane;
379         winding_t                       *w;
380         winding_t                       *front, *back;
381         winding_t                       *outside[ MAX_BRUSH_SIDES ];
382         int                                     numOutside;
383         mapDrawSurface_t        *newds;
384         
385         
386         /* dummy check */
387         if( ds->sideRef == NULL || ds->sideRef->side == NULL )
388                 return qfalse;
389         
390         /* initial setup */
391         w = WindingFromDrawSurf( ds );
392         numOutside = 0;
393         
394         /* chop by each brush side */
395         for( i = 0; i < b->numsides; i++ )
396         {
397                 /* get brush side and plane */
398                 s = &b->sides[ i ];
399                 if( s->backSide )
400                         continue;
401                 plane = &mapplanes[ s->planenum ];
402                 
403                 /* handle coplanar outfacing (don't fog) */
404                 if( ds->sideRef->side->planenum == s->planenum )
405                         return qfalse;
406                 
407                 /* handle coplanar infacing (keep inside) */
408                 if( (ds->sideRef->side->planenum ^ 1) == s->planenum )
409                         continue;
410                 
411                 /* general case */
412                 ClipWindingEpsilon( w, plane->normal, plane->dist, ON_EPSILON, &front, &back );
413                 FreeWinding( w );
414                 
415                 if( back == NULL )
416                 {
417                         /* nothing actually contained inside */
418                         for( j = 0; j < numOutside; j++ )
419                                 FreeWinding( outside[ j ] );
420                         return qfalse;
421                 }
422                 
423                 if( front != NULL )
424                 {
425                         if( numOutside == MAX_BRUSH_SIDES )
426                                 Error( "MAX_BRUSH_SIDES" );
427                         outside[ numOutside ] = front;
428                         numOutside++;
429                 }
430                 
431                 w = back;
432         }
433         
434         /* fixme: celshaded surface fragment errata */
435         
436         /* all of outside fragments become seperate drawsurfs */
437         numFogFragments += numOutside;
438         s = ds->sideRef->side;
439         for( i = 0; i < numOutside; i++ )
440         {
441                 newds = DrawSurfaceForSide( e, ds->mapBrush, s, outside[ i ] );
442                 newds->fogNum = ds->fogNum;
443                 FreeWinding( outside[ i ] );
444         }
445         
446         /* ydnar: the old code neglected to snap to 0.125 for the fragment
447                   inside the fog brush, leading to sparklies. this new code does
448                           the right thing and uses the original surface's brush side */
449         
450         /* build a drawsurf for it */
451         newds = DrawSurfaceForSide( e, ds->mapBrush, s, w );
452         if( newds == NULL )
453                 return qfalse;
454         
455         /* copy new to original */
456         ClearSurface( ds );
457         memcpy( ds, newds, sizeof( mapDrawSurface_t ) );
458         
459         /* didn't really add a new drawsurface... :) */
460         numMapDrawSurfs--;
461         
462         /* return ok */
463         return qtrue;
464 }
465
466
467
468 /*
469 FogDrawSurfaces()
470 call after the surface list has been pruned, before tjunction fixing
471 */
472
473 void FogDrawSurfaces( entity_t *e )
474 {
475         int                                     i, j, k, fogNum;
476         fog_t                           *fog;
477         mapDrawSurface_t        *ds;
478         vec3_t                          mins, maxs;
479         int                                     fogged, numFogged;
480         int                                     numBaseDrawSurfs;
481         
482         
483         /* note it */
484         Sys_FPrintf( SYS_VRB, "----- FogDrawSurfs -----\n" );
485         
486         /* reset counters */
487         numFogged = 0;
488         numFogFragments = 0;
489         
490         /* walk fog list */
491         for( fogNum = 0; fogNum < numMapFogs; fogNum++ )
492         {
493                 /* get fog */
494                 fog = &mapFogs[ fogNum ];
495                 
496                 /* clip each surface into this, but don't clip any of the resulting fragments to the same brush */
497                 numBaseDrawSurfs = numMapDrawSurfs;
498                 for( i = 0; i < numBaseDrawSurfs; i++ )
499                 {
500                         /* get the drawsurface */
501                         ds = &mapDrawSurfs[ i ];
502                         
503                         /* no fog? */
504                         if( ds->shaderInfo->noFog )
505                                 continue;
506                         
507                         /* global fog doesn't have a brush */
508                         if( fog->brush == NULL )
509                         {
510                                 /* don't re-fog already fogged surfaces */
511                                 if( ds->fogNum >= 0 )
512                                         continue;
513                                 fogged = 1;
514                         }
515                         else
516                         {
517                                 /* find drawsurface bounds */
518                                 ClearBounds( mins, maxs );
519                                 for( j = 0; j < ds->numVerts; j++ )
520                                         AddPointToBounds( ds->verts[ j ].xyz, mins, maxs );
521
522                                 /* check against the fog brush */
523                                 for( k = 0; k < 3; k++ )
524                                 {
525                                         if( mins[ k ] > fog->brush->maxs[ k ] )
526                                                 break;
527                                         if( maxs[ k ] < fog->brush->mins[ k ] )
528                                                 break;
529                                 }
530                                 
531                                 /* no intersection? */
532                                 if( k < 3 )
533                                         continue;
534                                 
535                                 /* ydnar: gs mods: handle the various types of surfaces */
536                                 switch( ds->type )
537                                 {
538                                         /* handle brush faces */
539                                         case SURFACE_FACE:
540                                                 fogged = ChopFaceSurfaceByBrush( e, ds, fog->brush );
541                                                 break;
542                                         
543                                         /* handle patches */
544                                         case SURFACE_PATCH:
545                                                 fogged = ChopPatchSurfaceByBrush( e, ds, fog->brush );
546                                                 break;
547                                         
548                                         /* handle triangle surfaces (fixme: split triangle surfaces) */
549                                         case SURFACE_TRIANGLES:
550                                         case SURFACE_FORCED_META:
551                                         case SURFACE_META:
552                                                 fogged = 1;
553                                                 break;
554
555                                         /* no fogging */
556                                         default:
557                                                 fogged = 0;
558                                                 break;
559                                 }
560                         }
561                         
562                         /* is this surface fogged? */
563                         if( fogged )
564                         {
565                                 numFogged += fogged;
566                                 ds->fogNum = fogNum;
567                         }
568                 }
569         }
570         
571         /* emit some statistics */
572         Sys_FPrintf( SYS_VRB, "%9d fog polygon fragments\n", numFogFragments );
573         Sys_FPrintf( SYS_VRB, "%9d fog patch fragments\n", numFogPatchFragments );
574         Sys_FPrintf( SYS_VRB, "%9d fogged drawsurfs\n", numFogged );
575 }
576
577
578
579 /*
580 FogForPoint() - ydnar
581 gets the fog number for a point in space
582 */
583
584 int FogForPoint( vec3_t point, float epsilon )
585 {
586         int                             fogNum, i, j;
587         float                   dot;
588         qboolean                inside;
589         brush_t                 *brush;
590         plane_t                 *plane;
591         
592         
593         /* start with bogus fog num */
594         fogNum = defaultFogNum;
595         
596         /* walk the list of fog volumes */
597         for( i = 0; i < numMapFogs; i++ )
598         {
599                 /* sof2: global fog doesn't reference a brush */
600                 if( mapFogs[ i ].brush == NULL )
601                 {
602                         fogNum = i;
603                         continue;
604                 }
605                 
606                 /* get fog brush */
607                 brush = mapFogs[ i ].brush;
608                 
609                 /* check point against all planes */
610                 inside = qtrue;
611                 for( j = 0; j < brush->numsides && inside; j++ )
612                 {
613                         plane = &mapplanes[ brush->sides[ j ].planenum ];       /* note usage of map planes here */
614                         dot = DotProduct( point, plane->normal );
615                         dot -= plane->dist;
616                         if( dot > epsilon )
617                                 inside = qfalse;
618                 }
619                 
620                 /* if inside, return the fog num */
621                 if( inside )
622                 {
623                         //%     Sys_Printf( "FogForPoint: %f, %f, %f in fog %d\n", point[ 0 ], point[ 1 ], point[ 2 ], i );
624                         return i;
625                 }
626         }
627         
628         /* if the point made it this far, it's not inside any fog volumes (or inside global fog) */
629         return fogNum;
630 }
631
632
633
634 /*
635 FogForBounds() - ydnar
636 gets the fog number for a bounding box
637 */
638
639 int FogForBounds( vec3_t mins, vec3_t maxs, float epsilon )
640 {
641         int                             fogNum, i, j;
642         float                   highMin, lowMax, volume, bestVolume;
643         vec3_t                  fogMins, fogMaxs, overlap;
644         brush_t                 *brush;
645         
646         
647         /* start with bogus fog num */
648         fogNum = defaultFogNum;
649         
650         /* init */
651         bestVolume = 0.0f;
652         
653         /* walk the list of fog volumes */
654         for( i = 0; i < numMapFogs; i++ )
655         {
656                 /* sof2: global fog doesn't reference a brush */
657                 if( mapFogs[ i ].brush == NULL )
658                 {
659                         fogNum = i;
660                         continue;
661                 }
662                 
663                 /* get fog brush */
664                 brush = mapFogs[ i ].brush;
665                 
666                 /* get bounds */
667                 fogMins[ 0 ] = brush->mins[ 0 ] - epsilon;
668                 fogMins[ 1 ] = brush->mins[ 1 ] - epsilon;
669                 fogMins[ 2 ] = brush->mins[ 2 ] - epsilon;
670                 fogMaxs[ 0 ] = brush->maxs[ 0 ] + epsilon;
671                 fogMaxs[ 1 ] = brush->maxs[ 1 ] + epsilon;
672                 fogMaxs[ 2 ] = brush->maxs[ 2 ] + epsilon;
673                 
674                 /* check against bounds */
675                 for( j = 0; j < 3; j++ )
676                 {
677                         if( mins[ j ] > fogMaxs[ j ] || maxs[ j ] < fogMins[ j ] )
678                                 break;
679                         highMin = mins[ j ] > fogMins[ j ] ? mins[ j ] : fogMins[ j ];
680                         lowMax = maxs[ j ] < fogMaxs[ j ] ? maxs[ j ] : fogMaxs[ j ];
681                         overlap[ j ] = lowMax - highMin;
682                         if( overlap[ j ] < 1.0f )
683                                 overlap[ j ] = 1.0f;
684                 }
685                 
686                 /* no overlap */
687                 if( j < 3 )
688                         continue;
689                 
690                 /* get volume */
691                 volume = overlap[ 0 ] * overlap[ 1 ] * overlap[ 2 ];
692                 
693                 /* test against best volume */
694                 if( volume > bestVolume )
695                 {
696                         bestVolume = volume;
697                         fogNum = i;
698                 }
699         }
700         
701         /* if the point made it this far, it's not inside any fog volumes (or inside global fog) */
702         return fogNum;
703 }
704
705
706
707 /*
708 CreateMapFogs() - ydnar
709 generates a list of map fogs
710 */
711
712 void CreateMapFogs( void )
713 {
714         int                     i;
715         entity_t        *entity;
716         brush_t         *brush;
717         fog_t           *fog;
718         vec3_t          invFogDir;
719         const char      *globalFog;
720         
721         
722         /* skip? */
723         if( nofog )
724                 return;
725         
726         /* note it */
727         Sys_FPrintf( SYS_VRB, "--- CreateMapFogs ---\n" );
728         
729         /* walk entities */
730         for( i = 0; i < numEntities; i++ )
731         {
732                 /* get entity */
733                 entity = &entities[ i ];
734                 
735                 /* walk entity brushes */
736                 for( brush = entity->brushes; brush != NULL; brush = brush->next )
737                 {
738                         /* ignore non-fog brushes */
739                         if( brush->contentShader->fogParms == qfalse )
740                                 continue;
741                         
742                         /* test limit */
743                         if( numMapFogs >= MAX_MAP_FOGS )
744                                 Error( "Exceeded MAX_MAP_FOGS (%d)", MAX_MAP_FOGS );
745                         
746                         /* set up fog */
747                         fog = &mapFogs[ numMapFogs++ ];
748                         fog->si = brush->contentShader;
749                         fog->brush = brush;
750                         fog->visibleSide = -1;
751                         
752                         /* if shader specifies an explicit direction, then find a matching brush side with an opposed normal */
753                         if( VectorLength( fog->si->fogDir ) )
754                         {
755                                 /* flip it */
756                                 VectorScale( fog->si->fogDir, -1.0f, invFogDir );
757                                 
758                                 /* find the brush side */
759                                 for( i = 0; i < brush->numsides; i++ )
760                                 {
761                                         if( VectorCompare( invFogDir, mapplanes[ brush->sides[ i ].planenum ].normal ) )
762                                         {
763                                                 fog->visibleSide = i;
764                                                 //%     Sys_Printf( "Brush num: %d Side num: %d\n", fog->brushNum, fog->visibleSide );
765                                                 break;
766                                         }
767                                 }
768                         }
769                 }
770         }
771         
772         /* ydnar: global fog */
773         globalFog = ValueForKey( &entities[ 0 ], "_fog" );
774         if( globalFog[ 0 ] == '\0' )
775                 globalFog = ValueForKey( &entities[ 0 ], "fog" );
776         if( globalFog[ 0 ] != '\0' )
777         {
778                 /* test limit */
779                 if( numMapFogs >= MAX_MAP_FOGS )
780                         Error( "Exceeded MAX_MAP_FOGS (%d) trying to add global fog", MAX_MAP_FOGS );
781                 
782                 /* note it */
783                 Sys_FPrintf( SYS_VRB, "Map has global fog shader %s\n", globalFog );
784                 
785                 /* set up fog */
786                 fog = &mapFogs[ numMapFogs++ ];
787                 fog->si = ShaderInfoForShader( globalFog );
788                 if( fog->si == NULL )
789                         Error( "Invalid shader \"%s\" referenced trying to add global fog", globalFog );
790                 fog->brush = NULL;
791                 fog->visibleSide = -1;
792                 
793                 /* set as default fog */
794                 defaultFogNum = numMapFogs - 1;
795                 
796                 /* mark all worldspawn brushes as fogged */
797                 for( brush = entities[ 0 ].brushes; brush != NULL; brush = brush->next )
798                         ApplySurfaceParm( "fog", &brush->contentFlags, NULL, &brush->compileFlags );
799         }
800         
801         /* emit some stats */
802         Sys_FPrintf( SYS_VRB, "%9d fogs\n", numMapFogs );
803 }
804