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 ){
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 );
68 chops a mesh by a plane
71 void SplitMeshByPlane( mesh_t *in, vec3_t normal, float dist, mesh_t **front, mesh_t **back ){
73 float d[MAX_PATCH_SIZE][MAX_PATCH_SIZE];
74 bspDrawVert_t *dv, *v1, *v2;
75 int c_front, c_back, c_on;
79 int frontAprox, backAprox;
81 for ( i = 0 ; i < 2 ; i++ ) {
86 for ( h = 0 ; h < in->height ; h++ ) {
87 for ( w = 0 ; w < in->width ; w++, dv++ ) {
88 d[h][w] = DotProduct( dv->xyz, normal ) - dist;
89 if ( d[h][w] > ON_EPSILON ) {
92 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 ) ) {
168 if ( f->width > MAX_PATCH_SIZE ) {
169 Error( "MAX_PATCH_SIZE after split" );
171 f->height = in->height;
172 f->verts = safe_malloc( sizeof( f->verts[0] ) * f->width * f->height );
174 b = safe_malloc( sizeof( *b ) );
175 b->width = in->width - split;
176 if ( !( b->width & 1 ) ) {
183 if ( b->width > MAX_PATCH_SIZE ) {
184 Error( "MAX_PATCH_SIZE after split" );
186 b->height = in->height;
187 b->verts = safe_malloc( sizeof( b->verts[0] ) * b->width * b->height );
198 // distribute the points
199 for ( w = 0 ; w < in->width ; w++ ) {
200 for ( h = 0 ; h < in->height ; h++ ) {
202 f->verts[ h * f->width + w ] = in->verts[ h * in->width + w ];
205 b->verts[ h * b->width + w - split + backAprox ] = in->verts[ h * in->width + w ];
210 // clip the crossing line
211 for ( h = 0; h < in->height; h++ )
213 dv = &f->verts[ h * f->width + split + 1 ];
214 v1 = &in->verts[ h * in->width + split ];
215 v2 = &in->verts[ h * in->width + split + 1 ];
217 frac = d[h][split] / ( d[h][split] - d[h][split + 1] );
220 //% for( i = 0; i < 10; i++ )
221 //% dv->xyz[ i ] = v1->xyz[ i ] + frac * (v2->xyz[ i ] - v1->xyz[ i ]);
222 //% dv->xyz[10] = 0; // set all 4 colors to 0
223 LerpDrawVertAmount( v1, v2, frac, dv );
226 f->verts[ h * f->width + split + 2 ] = *dv;
228 b->verts[ h * b->width ] = *dv;
230 b->verts[ h * b->width + 1 ] = *dv;
248 ChopPatchSurfaceByBrush()
249 chops a patch up by a fog brush
252 qboolean ChopPatchSurfaceByBrush( entity_t *e, mapDrawSurface_t *ds, brush_t *b ){
256 mesh_t *outside[MAX_BRUSH_SIDES];
258 mesh_t *m, *front, *back;
259 mapDrawSurface_t *newds;
261 m = DrawSurfToMesh( ds );
264 // only split by the top and bottom planes to avoid
265 // some messy patch clipping issues
267 for ( i = 4 ; i <= 5 ; i++ ) {
269 plane = &mapplanes[ s->planenum ];
271 SplitMeshByPlane( m, plane->normal, plane->dist, &front, &back );
274 // nothing actually contained inside
275 for ( j = 0 ; j < numOutside ; j++ ) {
276 FreeMesh( outside[j] );
283 if ( numOutside == MAX_BRUSH_SIDES ) {
284 Error( "MAX_BRUSH_SIDES" );
286 outside[ numOutside ] = front;
291 /* all of outside fragments become seperate drawsurfs */
292 numFogPatchFragments += numOutside;
293 for ( i = 0; i < numOutside; i++ )
295 /* transpose and invert the chopped patch (fixes potential crash. fixme: why?) */
296 outside[ i ] = TransposeMesh( outside[ i ] );
297 InvertMesh( outside[ i ] );
299 /* ydnar: do this the hacky right way */
300 newds = AllocDrawSurface( SURFACE_PATCH );
301 memcpy( newds, ds, sizeof( *ds ) );
302 newds->patchWidth = outside[ i ]->width;
303 newds->patchHeight = outside[ i ]->height;
304 newds->numVerts = outside[ i ]->width * outside[ i ]->height;
305 newds->verts = safe_malloc( newds->numVerts * sizeof( *newds->verts ) );
306 memcpy( newds->verts, outside[ i ]->verts, newds->numVerts * sizeof( *newds->verts ) );
308 /* free the source mesh */
309 FreeMesh( outside[ i ] );
312 /* only rejigger this patch if it was chopped */
313 //% Sys_Printf( "Inside: %d x %d\n", m->width, m->height );
314 if ( numOutside > 0 ) {
315 /* transpose and invert the chopped patch (fixes potential crash. fixme: why?) */
316 m = TransposeMesh( m );
319 /* replace ds with m */
320 ds->patchWidth = m->width;
321 ds->patchHeight = m->height;
322 ds->numVerts = m->width * m->height;
324 ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );
325 memcpy( ds->verts, m->verts, ds->numVerts * sizeof( *ds->verts ) );
328 /* free the source mesh and return */
336 WindingFromDrawSurf()
337 creates a winding from a surface's verts
340 winding_t *WindingFromDrawSurf( mapDrawSurface_t *ds ){
344 // we use the first point of the surface, maybe something more clever would be useful
345 // (actually send the whole draw surface would be cool?)
346 if ( ds->numVerts >= MAX_POINTS_ON_WINDING ) {
347 int max = ds->numVerts;
354 for ( i = 0 ; i < max ; i++ ) {
355 VectorCopy( ds->verts[i].xyz, p[i] );
358 xml_Winding( "WindingFromDrawSurf failed: MAX_POINTS_ON_WINDING exceeded", p, max, qtrue );
361 w = AllocWinding( ds->numVerts );
362 w->numpoints = ds->numVerts;
363 for ( i = 0 ; i < ds->numVerts ; i++ ) {
364 VectorCopy( ds->verts[i].xyz, w->p[i] );
372 ChopFaceSurfaceByBrush()
373 chops up a face drawsurface by a fog brush, with a potential fragment left inside
376 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 ) {
393 w = WindingFromDrawSurf( ds );
396 /* chop by each brush side */
397 for ( i = 0; i < b->numsides; i++ )
399 /* get brush side and plane */
401 plane = &mapplanes[ s->planenum ];
403 /* handle coplanar outfacing (don't fog) */
404 if ( ds->sideRef->side->planenum == s->planenum ) {
408 /* handle coplanar infacing (keep inside) */
409 if ( ( ds->sideRef->side->planenum ^ 1 ) == s->planenum ) {
414 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 */
417 if ( back == NULL ) {
418 /* nothing actually contained inside */
419 for ( j = 0; j < numOutside; j++ )
420 FreeWinding( outside[ j ] );
424 if ( front != NULL ) {
425 if ( numOutside == MAX_BRUSH_SIDES ) {
426 Error( "MAX_BRUSH_SIDES" );
428 outside[ numOutside ] = front;
435 /* fixme: celshaded surface fragment errata */
437 /* all of outside fragments become seperate drawsurfs */
438 numFogFragments += numOutside;
439 s = ds->sideRef->side;
440 for ( i = 0; i < numOutside; i++ )
442 newds = DrawSurfaceForSide( e, ds->mapBrush, s, outside[ i ] );
443 newds->fogNum = ds->fogNum;
444 FreeWinding( outside[ i ] );
447 /* ydnar: the old code neglected to snap to 0.125 for the fragment
448 inside the fog brush, leading to sparklies. this new code does
449 the right thing and uses the original surface's brush side */
451 /* build a drawsurf for it */
452 newds = DrawSurfaceForSide( e, ds->mapBrush, s, w );
453 if ( newds == NULL ) {
457 /* copy new to original */
459 memcpy( ds, newds, sizeof( mapDrawSurface_t ) );
461 /* didn't really add a new drawsurface... :) */
472 call after the surface list has been pruned, before tjunction fixing
475 void FogDrawSurfaces( entity_t *e ){
478 mapDrawSurface_t *ds;
480 int fogged, numFogged;
481 int numBaseDrawSurfs;
485 Sys_FPrintf( SYS_VRB, "----- FogDrawSurfs -----\n" );
492 for ( fogNum = 0; fogNum < numMapFogs; fogNum++ )
495 fog = &mapFogs[ fogNum ];
497 /* clip each surface into this, but don't clip any of the resulting fragments to the same brush */
498 numBaseDrawSurfs = numMapDrawSurfs;
499 for ( i = 0; i < numBaseDrawSurfs; i++ )
501 /* get the drawsurface */
502 ds = &mapDrawSurfs[ i ];
505 if ( ds->shaderInfo->noFog ) {
509 /* global fog doesn't have a brush */
510 if ( fog->brush == NULL ) {
511 /* don't re-fog already fogged surfaces */
512 if ( ds->fogNum >= 0 ) {
519 /* find drawsurface bounds */
520 ClearBounds( mins, maxs );
521 for ( j = 0; j < ds->numVerts; j++ )
522 AddPointToBounds( ds->verts[ j ].xyz, mins, maxs );
524 /* check against the fog brush */
525 for ( k = 0; k < 3; k++ )
527 if ( mins[ k ] > fog->brush->maxs[ k ] ) {
530 if ( maxs[ k ] < fog->brush->mins[ k ] ) {
535 /* no intersection? */
540 /* ydnar: gs mods: handle the various types of surfaces */
543 /* handle brush faces */
545 fogged = ChopFaceSurfaceByBrush( e, ds, fog->brush );
550 fogged = ChopPatchSurfaceByBrush( e, ds, fog->brush );
553 /* handle triangle surfaces (fixme: split triangle surfaces) */
554 case SURFACE_TRIANGLES:
555 case SURFACE_FORCED_META:
567 /* is this surface fogged? */
575 /* emit some statistics */
576 Sys_FPrintf( SYS_VRB, "%9d fog polygon fragments\n", numFogFragments );
577 Sys_FPrintf( SYS_VRB, "%9d fog patch fragments\n", numFogPatchFragments );
578 Sys_FPrintf( SYS_VRB, "%9d fogged drawsurfs\n", numFogged );
584 FogForPoint() - ydnar
585 gets the fog number for a point in space
588 int FogForPoint( vec3_t point, float epsilon ){
596 /* start with bogus fog num */
597 fogNum = defaultFogNum;
599 /* walk the list of fog volumes */
600 for ( i = 0; i < numMapFogs; i++ )
602 /* sof2: global fog doesn't reference a brush */
603 if ( mapFogs[ i ].brush == NULL ) {
609 brush = mapFogs[ i ].brush;
611 /* check point against all planes */
613 for ( j = 0; j < brush->numsides && inside; j++ )
615 plane = &mapplanes[ brush->sides[ j ].planenum ]; /* note usage of map planes here */
616 dot = DotProduct( point, plane->normal );
618 if ( dot > epsilon ) {
623 /* if inside, return the fog num */
625 //% Sys_Printf( "FogForPoint: %f, %f, %f in fog %d\n", point[ 0 ], point[ 1 ], point[ 2 ], i );
630 /* if the point made it this far, it's not inside any fog volumes (or inside global fog) */
637 FogForBounds() - ydnar
638 gets the fog number for a bounding box
641 int FogForBounds( vec3_t mins, vec3_t maxs, float epsilon ){
643 float highMin, lowMax, volume, bestVolume;
644 vec3_t fogMins, fogMaxs, overlap;
648 /* start with bogus fog num */
649 fogNum = defaultFogNum;
654 /* walk the list of fog volumes */
655 for ( i = 0; i < numMapFogs; i++ )
657 /* sof2: global fog doesn't reference a brush */
658 if ( mapFogs[ i ].brush == NULL ) {
664 brush = mapFogs[ i ].brush;
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;
674 /* check against bounds */
675 for ( j = 0; j < 3; j++ )
677 if ( mins[ j ] > fogMaxs[ j ] || maxs[ j ] < fogMins[ j ] ) {
680 highMin = mins[ j ] > fogMins[ j ] ? mins[ j ] : fogMins[ j ];
681 lowMax = maxs[ j ] < fogMaxs[ j ] ? maxs[ j ] : fogMaxs[ j ];
682 overlap[ j ] = lowMax - highMin;
683 if ( overlap[ j ] < 1.0f ) {
694 volume = overlap[ 0 ] * overlap[ 1 ] * overlap[ 2 ];
696 /* test against best volume */
697 if ( volume > bestVolume ) {
703 /* if the point made it this far, it's not inside any fog volumes (or inside global fog) */
710 CreateMapFogs() - ydnar
711 generates a list of map fogs
714 void CreateMapFogs( void ){
720 const char *globalFog;
729 Sys_FPrintf( SYS_VRB, "--- CreateMapFogs ---\n" );
732 for ( i = 0; i < numEntities; i++ )
735 entity = &entities[ i ];
737 /* walk entity brushes */
738 for ( brush = entity->brushes; brush != NULL; brush = brush->next )
740 /* ignore non-fog brushes */
741 if ( brush->contentShader->fogParms == qfalse ) {
746 if ( numMapFogs >= MAX_MAP_FOGS ) {
747 Error( "Exceeded MAX_MAP_FOGS (%d)", MAX_MAP_FOGS );
751 fog = &mapFogs[ numMapFogs++ ];
752 fog->si = brush->contentShader;
754 fog->visibleSide = -1;
756 /* if shader specifies an explicit direction, then find a matching brush side with an opposed normal */
757 if ( VectorLength( fog->si->fogDir ) ) {
759 VectorScale( fog->si->fogDir, -1.0f, invFogDir );
761 /* find the brush side */
762 for ( j = 0; j < brush->numsides; j++ )
764 if ( VectorCompare( invFogDir, mapplanes[ brush->sides[ j ].planenum ].normal ) ) {
765 fog->visibleSide = j;
766 //% Sys_Printf( "Brush num: %d Side num: %d\n", fog->brushNum, fog->visibleSide );
774 /* ydnar: global fog */
775 globalFog = ValueForKey( &entities[ 0 ], "_fog" );
776 if ( globalFog[ 0 ] == '\0' ) {
777 globalFog = ValueForKey( &entities[ 0 ], "fog" );
779 if ( globalFog[ 0 ] != '\0' ) {
781 if ( numMapFogs >= MAX_MAP_FOGS ) {
782 Error( "Exceeded MAX_MAP_FOGS (%d) trying to add global fog", MAX_MAP_FOGS );
786 Sys_FPrintf( SYS_VRB, "Map has global fog shader %s\n", globalFog );
789 fog = &mapFogs[ numMapFogs++ ];
790 fog->si = ShaderInfoForShaderNull( globalFog );
791 if ( fog->si == NULL ) {
792 Error( "Invalid shader \"%s\" referenced trying to add global fog", globalFog );
795 fog->visibleSide = -1;
797 /* set as default fog */
798 defaultFogNum = numMapFogs - 1;
800 /* mark all worldspawn brushes as fogged */
801 for ( brush = entities[ 0 ].brushes; brush != NULL; brush = brush->next )
802 ApplySurfaceParm( "fog", &brush->contentFlags, NULL, &brush->compileFlags );
805 /* emit some stats */
806 Sys_FPrintf( SYS_VRB, "%9d fogs\n", numMapFogs );