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