1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
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.
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.
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
22 ----------------------------------------------------------------------------------
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."
27 ------------------------------------------------------------------------------- */
42 int numFogPatchFragments;
48 converts a patch drawsurface to a mesh_t
51 mesh_t *DrawSurfToMesh( mapDrawSurface_t *ds )
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 );
69 chops a mesh by a plane
72 void SplitMeshByPlane( mesh_t *in, vec3_t normal, float dist, mesh_t **front, mesh_t **back )
75 float d[MAX_PATCH_SIZE][MAX_PATCH_SIZE];
76 bspDrawVert_t *dv, *v1, *v2;
77 int c_front, c_back, c_on;
81 int frontAprox, backAprox;
83 for ( i = 0 ; i < 2 ; i++ ) {
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 ) {
93 } else if ( d[h][w] < -ON_EPSILON ) {
113 // find a split point
115 for ( w = 0 ; w < in->width -1 ; w++ ) {
116 if ( ( d[0][w] < 0 ) != ( d[0][w+1] < 0 ) ) {
126 Sys_FPrintf (SYS_VRB, "No crossing points in patch\n");
131 in = TransposeMesh( in );
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 ) ) {
141 Sys_Printf( "multiple crossing points for patch -- can't clip\n");
147 if ( ( d[h][split] < 0 ) == ( d[h][split+1] < 0 ) ) {
148 Sys_Printf( "differing crossing points for patch -- can't clip\n");
158 // create two new meshes
159 f = safe_malloc( sizeof( *f ) );
160 f->width = split + 2;
161 if ( ! (f->width & 1) ) {
167 if ( f->width > MAX_PATCH_SIZE ) {
168 Error( "MAX_PATCH_SIZE after split");
170 f->height = in->height;
171 f->verts = safe_malloc( sizeof(f->verts[0]) * f->width * f->height );
173 b = safe_malloc( sizeof( *b ) );
174 b->width = in->width - split;
175 if ( ! (b->width & 1) ) {
181 if ( b->width > MAX_PATCH_SIZE ) {
182 Error( "MAX_PATCH_SIZE after split");
184 b->height = in->height;
185 b->verts = safe_malloc( sizeof(b->verts[0]) * b->width * b->height );
195 // distribute the points
196 for ( w = 0 ; w < in->width ; w++ ) {
197 for ( h = 0 ; h < in->height ; h++ ) {
199 f->verts[ h * f->width + w ] = in->verts[ h * in->width + w ];
201 b->verts[ h * b->width + w - split + backAprox ] = in->verts[ h * in->width + w ];
206 // clip the crossing line
207 for ( h = 0; h < in->height; h++ )
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 ];
213 frac = d[h][split] / ( d[h][split] - d[h][split+1] );
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 );
222 f->verts[ h * f->width + split + 2 ] = *dv;
224 b->verts[ h * b->width ] = *dv;
226 b->verts[ h * b->width + 1 ] = *dv;
244 ChopPatchSurfaceByBrush()
245 chops a patch up by a fog brush
248 qboolean ChopPatchSurfaceByBrush( entity_t *e, mapDrawSurface_t *ds, brush_t *b )
253 mesh_t *outside[MAX_BRUSH_SIDES];
255 mesh_t *m, *front, *back;
256 mapDrawSurface_t *newds;
258 m = DrawSurfToMesh( ds );
261 // only split by the top and bottom planes to avoid
262 // some messy patch clipping issues
264 for ( i = 4 ; i <= 5 ; i++ ) {
266 plane = &mapplanes[ s->planenum ];
268 SplitMeshByPlane( m, plane->normal, plane->dist, &front, &back );
271 // nothing actually contained inside
272 for ( j = 0 ; j < numOutside ; j++ ) {
273 FreeMesh( outside[j] );
280 if ( numOutside == MAX_BRUSH_SIDES ) {
281 Error( "MAX_BRUSH_SIDES" );
283 outside[ numOutside ] = front;
288 /* all of outside fragments become seperate drawsurfs */
289 numFogPatchFragments += numOutside;
290 for( i = 0; i < numOutside; i++ )
292 /* transpose and invert the chopped patch (fixes potential crash. fixme: why?) */
293 outside[ i ] = TransposeMesh( outside[ i ] );
294 InvertMesh( outside[ i ] );
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 ) );
305 /* free the source mesh */
306 FreeMesh( outside[ i ] );
309 /* only rejigger this patch if it was chopped */
310 //% Sys_Printf( "Inside: %d x %d\n", m->width, m->height );
313 /* transpose and invert the chopped patch (fixes potential crash. fixme: why?) */
314 m = TransposeMesh( m );
317 /* replace ds with m */
318 ds->patchWidth = m->width;
319 ds->patchHeight = m->height;
320 ds->numVerts = m->width * m->height;
322 ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );
323 memcpy( ds->verts, m->verts, ds->numVerts * sizeof( *ds->verts ) );
326 /* free the source mesh and return */
334 WindingFromDrawSurf()
335 creates a winding from a surface's verts
338 winding_t *WindingFromDrawSurf( mapDrawSurface_t *ds )
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 )
347 int max = ds->numVerts;
353 for ( i = 0 ; i < max ; i++ ) {
354 VectorCopy( ds->verts[i].xyz, p[i] );
357 xml_Winding( "WindingFromDrawSurf failed: MAX_POINTS_ON_WINDING exceeded", p, max, qtrue );
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] );
371 ChopFaceSurfaceByBrush()
372 chops up a face drawsurface by a fog brush, with a potential fragment left inside
375 qboolean ChopFaceSurfaceByBrush( entity_t *e, mapDrawSurface_t *ds, brush_t *b )
381 winding_t *front, *back;
382 winding_t *outside[ MAX_BRUSH_SIDES ];
384 mapDrawSurface_t *newds;
388 if( ds->sideRef == NULL || ds->sideRef->side == NULL )
392 w = WindingFromDrawSurf( ds );
395 /* chop by each brush side */
396 for( i = 0; i < b->numsides; i++ )
398 /* get brush side and plane */
400 plane = &mapplanes[ s->planenum ];
402 /* handle coplanar outfacing (don't fog) */
403 if( ds->sideRef->side->planenum == s->planenum )
406 /* handle coplanar infacing (keep inside) */
407 if( (ds->sideRef->side->planenum ^ 1) == s->planenum )
411 ClipWindingEpsilonStrict( w, plane->normal, plane->dist, ON_EPSILON, &front, &back ); /* strict; if plane is "almost identical" to face, both ways to continue can be wrong, so we better not fog it */
416 /* nothing actually contained inside */
417 for( j = 0; j < numOutside; j++ )
418 FreeWinding( outside[ j ] );
424 if( numOutside == MAX_BRUSH_SIDES )
425 Error( "MAX_BRUSH_SIDES" );
426 outside[ numOutside ] = front;
433 /* fixme: celshaded surface fragment errata */
435 /* all of outside fragments become seperate drawsurfs */
436 numFogFragments += numOutside;
437 s = ds->sideRef->side;
438 for( i = 0; i < numOutside; i++ )
440 newds = DrawSurfaceForSide( e, ds->mapBrush, s, outside[ i ] );
441 newds->fogNum = ds->fogNum;
442 FreeWinding( outside[ i ] );
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 */
449 /* build a drawsurf for it */
450 newds = DrawSurfaceForSide( e, ds->mapBrush, s, w );
454 /* copy new to original */
456 memcpy( ds, newds, sizeof( mapDrawSurface_t ) );
458 /* didn't really add a new drawsurface... :) */
469 call after the surface list has been pruned, before tjunction fixing
472 void FogDrawSurfaces( entity_t *e )
476 mapDrawSurface_t *ds;
478 int fogged, numFogged;
479 int numBaseDrawSurfs;
483 Sys_FPrintf( SYS_VRB, "----- FogDrawSurfs -----\n" );
490 for( fogNum = 0; fogNum < numMapFogs; fogNum++ )
493 fog = &mapFogs[ fogNum ];
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++ )
499 /* get the drawsurface */
500 ds = &mapDrawSurfs[ i ];
503 if( ds->shaderInfo->noFog )
506 /* global fog doesn't have a brush */
507 if( fog->brush == NULL )
509 /* don't re-fog already fogged surfaces */
510 if( ds->fogNum >= 0 )
516 /* find drawsurface bounds */
517 ClearBounds( mins, maxs );
518 for( j = 0; j < ds->numVerts; j++ )
519 AddPointToBounds( ds->verts[ j ].xyz, mins, maxs );
521 /* check against the fog brush */
522 for( k = 0; k < 3; k++ )
524 if( mins[ k ] > fog->brush->maxs[ k ] )
526 if( maxs[ k ] < fog->brush->mins[ k ] )
530 /* no intersection? */
534 /* ydnar: gs mods: handle the various types of surfaces */
537 /* handle brush faces */
539 fogged = ChopFaceSurfaceByBrush( e, ds, fog->brush );
544 fogged = ChopPatchSurfaceByBrush( e, ds, fog->brush );
547 /* handle triangle surfaces (fixme: split triangle surfaces) */
548 case SURFACE_TRIANGLES:
549 case SURFACE_FORCED_META:
561 /* is this surface fogged? */
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 );
579 FogForPoint() - ydnar
580 gets the fog number for a point in space
583 int FogForPoint( vec3_t point, float epsilon )
592 /* start with bogus fog num */
593 fogNum = defaultFogNum;
595 /* walk the list of fog volumes */
596 for( i = 0; i < numMapFogs; i++ )
598 /* sof2: global fog doesn't reference a brush */
599 if( mapFogs[ i ].brush == NULL )
606 brush = mapFogs[ i ].brush;
608 /* check point against all planes */
610 for( j = 0; j < brush->numsides && inside; j++ )
612 plane = &mapplanes[ brush->sides[ j ].planenum ]; /* note usage of map planes here */
613 dot = DotProduct( point, plane->normal );
619 /* if inside, return the fog num */
622 //% Sys_Printf( "FogForPoint: %f, %f, %f in fog %d\n", point[ 0 ], point[ 1 ], point[ 2 ], i );
627 /* if the point made it this far, it's not inside any fog volumes (or inside global fog) */
634 FogForBounds() - ydnar
635 gets the fog number for a bounding box
638 int FogForBounds( vec3_t mins, vec3_t maxs, float epsilon )
641 float highMin, lowMax, volume, bestVolume;
642 vec3_t fogMins, fogMaxs, overlap;
646 /* start with bogus fog num */
647 fogNum = defaultFogNum;
652 /* walk the list of fog volumes */
653 for( i = 0; i < numMapFogs; i++ )
655 /* sof2: global fog doesn't reference a brush */
656 if( mapFogs[ i ].brush == NULL )
663 brush = mapFogs[ i ].brush;
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;
673 /* check against bounds */
674 for( j = 0; j < 3; j++ )
676 if( mins[ j ] > fogMaxs[ j ] || maxs[ j ] < fogMins[ j ] )
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 )
690 volume = overlap[ 0 ] * overlap[ 1 ] * overlap[ 2 ];
692 /* test against best volume */
693 if( volume > bestVolume )
700 /* if the point made it this far, it's not inside any fog volumes (or inside global fog) */
707 CreateMapFogs() - ydnar
708 generates a list of map fogs
711 void CreateMapFogs( void )
718 const char *globalFog;
726 Sys_FPrintf( SYS_VRB, "--- CreateMapFogs ---\n" );
729 for( i = 0; i < numEntities; i++ )
732 entity = &entities[ i ];
734 /* walk entity brushes */
735 for( brush = entity->brushes; brush != NULL; brush = brush->next )
737 /* ignore non-fog brushes */
738 if( brush->contentShader->fogParms == qfalse )
742 if( numMapFogs >= MAX_MAP_FOGS )
743 Error( "Exceeded MAX_MAP_FOGS (%d)", MAX_MAP_FOGS );
746 fog = &mapFogs[ numMapFogs++ ];
747 fog->si = brush->contentShader;
749 fog->visibleSide = -1;
751 /* if shader specifies an explicit direction, then find a matching brush side with an opposed normal */
752 if( VectorLength( fog->si->fogDir ) )
755 VectorScale( fog->si->fogDir, -1.0f, invFogDir );
757 /* find the brush side */
758 for( i = 0; i < brush->numsides; i++ )
760 if( VectorCompare( invFogDir, mapplanes[ brush->sides[ i ].planenum ].normal ) )
762 fog->visibleSide = i;
763 //% Sys_Printf( "Brush num: %d Side num: %d\n", fog->brushNum, fog->visibleSide );
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' )
778 if( numMapFogs >= MAX_MAP_FOGS )
779 Error( "Exceeded MAX_MAP_FOGS (%d) trying to add global fog", MAX_MAP_FOGS );
782 Sys_FPrintf( SYS_VRB, "Map has global fog shader %s\n", globalFog );
785 fog = &mapFogs[ numMapFogs++ ];
786 fog->si = ShaderInfoForShaderNull( globalFog );
787 if( fog->si == NULL )
788 Error( "Invalid shader \"%s\" referenced trying to add global fog", globalFog );
790 fog->visibleSide = -1;
792 /* set as default fog */
793 defaultFogNum = numMapFogs - 1;
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 );
800 /* emit some stats */
801 Sys_FPrintf( SYS_VRB, "%9d fogs\n", numMapFogs );