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