2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
\r
5 This file is part of GtkRadiant.
\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
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
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
21 ----------------------------------------------------------------------------------
\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
26 ------------------------------------------------------------------------------- */
\r
40 int numFogFragments;
\r
41 int numFogPatchFragments;
\r
47 converts a patch drawsurface to a mesh_t
\r
50 mesh_t *DrawSurfToMesh( mapDrawSurface_t *ds )
\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
68 chops a mesh by a plane
\r
71 void SplitMeshByPlane( mesh_t *in, vec3_t normal, float dist, mesh_t **front, mesh_t **back )
\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
80 int frontAprox, backAprox;
\r
82 for ( i = 0 ; i < 2 ; i++ ) {
\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
92 } else if ( d[h][w] < -ON_EPSILON ) {
\r
112 // find a split point
\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
123 if ( split == -1 ) {
\r
125 Sys_FPrintf (SYS_VRB, "No crossing points in patch\n");
\r
130 in = TransposeMesh( in );
\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
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
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
166 if ( f->width > MAX_PATCH_SIZE ) {
\r
167 Error( "MAX_PATCH_SIZE after split");
\r
169 f->height = in->height;
\r
170 f->verts = safe_malloc( sizeof(f->verts[0]) * f->width * f->height );
\r
172 b = safe_malloc( sizeof( *b ) );
\r
173 b->width = in->width - split;
\r
174 if ( ! (b->width & 1) ) {
\r
180 if ( b->width > MAX_PATCH_SIZE ) {
\r
181 Error( "MAX_PATCH_SIZE after split");
\r
183 b->height = in->height;
\r
184 b->verts = safe_malloc( sizeof(b->verts[0]) * b->width * b->height );
\r
186 if ( d[0][0] > 0 ) {
\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
200 b->verts[ h * b->width + w - split + backAprox ] = in->verts[ h * in->width + w ];
\r
205 // clip the crossing line
\r
206 for ( h = 0; h < in->height; h++ )
\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
212 frac = d[h][split] / ( d[h][split] - d[h][split+1] );
\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
220 if ( frontAprox ) {
\r
221 f->verts[ h * f->width + split + 2 ] = *dv;
\r
223 b->verts[ h * b->width ] = *dv;
\r
225 b->verts[ h * b->width + 1 ] = *dv;
\r
243 ChopPatchSurfaceByBrush()
\r
244 chops a patch up by a fog brush
\r
247 qboolean ChopPatchSurfaceByBrush( entity_t *e, mapDrawSurface_t *ds, brush_t *b )
\r
252 mesh_t *outside[MAX_BRUSH_SIDES];
\r
254 mesh_t *m, *front, *back;
\r
255 mapDrawSurface_t *newds;
\r
257 m = DrawSurfToMesh( ds );
\r
260 // only split by the top and bottom planes to avoid
\r
261 // some messy patch clipping issues
\r
263 for ( i = 4 ; i <= 5 ; i++ ) {
\r
264 s = &b->sides[ i ];
\r
265 plane = &mapplanes[ s->planenum ];
\r
267 SplitMeshByPlane( m, plane->normal, plane->dist, &front, &back );
\r
270 // nothing actually contained inside
\r
271 for ( j = 0 ; j < numOutside ; j++ ) {
\r
272 FreeMesh( outside[j] );
\r
279 if ( numOutside == MAX_BRUSH_SIDES ) {
\r
280 Error( "MAX_BRUSH_SIDES" );
\r
282 outside[ numOutside ] = front;
\r
287 /* all of outside fragments become seperate drawsurfs */
\r
288 numFogPatchFragments += numOutside;
\r
289 for( i = 0; i < numOutside; i++ )
\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
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
304 /* free the source mesh */
\r
305 FreeMesh( outside[ i ] );
\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
312 /* transpose and invert the chopped patch (fixes potential crash. fixme: why?) */
\r
313 m = TransposeMesh( m );
\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
321 ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );
\r
322 memcpy( ds->verts, m->verts, ds->numVerts * sizeof( *ds->verts ) );
\r
325 /* free the source mesh and return */
\r
333 WindingFromDrawSurf()
\r
334 creates a winding from a surface's verts
\r
337 winding_t *WindingFromDrawSurf( mapDrawSurface_t *ds )
\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
346 int max = ds->numVerts;
\r
352 for ( i = 0 ; i < max ; i++ ) {
\r
353 VectorCopy( ds->verts[i].xyz, p[i] );
\r
356 xml_Winding( "WindingFromDrawSurf failed: MAX_POINTS_ON_WINDING exceeded", p, max, qtrue );
\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
370 ChopFaceSurfaceByBrush()
\r
371 chops up a face drawsurface by a fog brush, with a potential fragment left inside
\r
374 qboolean ChopFaceSurfaceByBrush( entity_t *e, mapDrawSurface_t *ds, brush_t *b )
\r
380 winding_t *front, *back;
\r
381 winding_t *outside[ MAX_BRUSH_SIDES ];
\r
383 mapDrawSurface_t *newds;
\r
387 if( ds->sideRef == NULL || ds->sideRef->side == NULL )
\r
390 /* initial setup */
\r
391 w = WindingFromDrawSurf( ds );
\r
394 /* chop by each brush side */
\r
395 for( i = 0; i < b->numsides; i++ )
\r
397 /* get brush side and plane */
\r
398 s = &b->sides[ i ];
\r
401 plane = &mapplanes[ s->planenum ];
\r
403 /* handle coplanar outfacing (don't fog) */
\r
404 if( ds->sideRef->side->planenum == s->planenum )
\r
407 /* handle coplanar infacing (keep inside) */
\r
408 if( (ds->sideRef->side->planenum ^ 1) == s->planenum )
\r
412 ClipWindingEpsilon( w, plane->normal, plane->dist, ON_EPSILON, &front, &back );
\r
417 /* nothing actually contained inside */
\r
418 for( j = 0; j < numOutside; j++ )
\r
419 FreeWinding( outside[ j ] );
\r
423 if( front != NULL )
\r
425 if( numOutside == MAX_BRUSH_SIDES )
\r
426 Error( "MAX_BRUSH_SIDES" );
\r
427 outside[ numOutside ] = front;
\r
434 /* fixme: celshaded surface fragment errata */
\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
441 newds = DrawSurfaceForSide( e, ds->mapBrush, s, outside[ i ] );
\r
442 newds->fogNum = ds->fogNum;
\r
443 FreeWinding( outside[ i ] );
\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
450 /* build a drawsurf for it */
\r
451 newds = DrawSurfaceForSide( e, ds->mapBrush, s, w );
\r
452 if( newds == NULL )
\r
455 /* copy new to original */
\r
456 ClearSurface( ds );
\r
457 memcpy( ds, newds, sizeof( mapDrawSurface_t ) );
\r
459 /* didn't really add a new drawsurface... :) */
\r
470 call after the surface list has been pruned, before tjunction fixing
\r
473 void FogDrawSurfaces( entity_t *e )
\r
475 int i, j, k, fogNum;
\r
477 mapDrawSurface_t *ds;
\r
479 int fogged, numFogged;
\r
480 int numBaseDrawSurfs;
\r
484 Sys_FPrintf( SYS_VRB, "----- FogDrawSurfs -----\n" );
\r
486 /* reset counters */
\r
488 numFogFragments = 0;
\r
490 /* walk fog list */
\r
491 for( fogNum = 0; fogNum < numMapFogs; fogNum++ )
\r
494 fog = &mapFogs[ fogNum ];
\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
500 /* get the drawsurface */
\r
501 ds = &mapDrawSurfs[ i ];
\r
504 if( ds->shaderInfo->noFog )
\r
507 /* global fog doesn't have a brush */
\r
508 if( fog->brush == NULL )
\r
510 /* don't re-fog already fogged surfaces */
\r
511 if( ds->fogNum >= 0 )
\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
522 /* check against the fog brush */
\r
523 for( k = 0; k < 3; k++ )
\r
525 if( mins[ k ] > fog->brush->maxs[ k ] )
\r
527 if( maxs[ k ] < fog->brush->mins[ k ] )
\r
531 /* no intersection? */
\r
535 /* ydnar: gs mods: handle the various types of surfaces */
\r
538 /* handle brush faces */
\r
540 fogged = ChopFaceSurfaceByBrush( e, ds, fog->brush );
\r
543 /* handle patches */
\r
544 case SURFACE_PATCH:
\r
545 fogged = ChopPatchSurfaceByBrush( e, ds, fog->brush );
\r
548 /* handle triangle surfaces (fixme: split triangle surfaces) */
\r
549 case SURFACE_TRIANGLES:
\r
550 case SURFACE_FORCED_META:
\r
562 /* is this surface fogged? */
\r
565 numFogged += fogged;
\r
566 ds->fogNum = fogNum;
\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
580 FogForPoint() - ydnar
\r
581 gets the fog number for a point in space
\r
584 int FogForPoint( vec3_t point, float epsilon )
\r
593 /* start with bogus fog num */
\r
594 fogNum = defaultFogNum;
\r
596 /* walk the list of fog volumes */
\r
597 for( i = 0; i < numMapFogs; i++ )
\r
599 /* sof2: global fog doesn't reference a brush */
\r
600 if( mapFogs[ i ].brush == NULL )
\r
606 /* get fog brush */
\r
607 brush = mapFogs[ i ].brush;
\r
609 /* check point against all planes */
\r
611 for( j = 0; j < brush->numsides && inside; j++ )
\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
620 /* if inside, return the fog num */
\r
623 //% Sys_Printf( "FogForPoint: %f, %f, %f in fog %d\n", point[ 0 ], point[ 1 ], point[ 2 ], i );
\r
628 /* if the point made it this far, it's not inside any fog volumes (or inside global fog) */
\r
635 FogForBounds() - ydnar
\r
636 gets the fog number for a bounding box
\r
639 int FogForBounds( vec3_t mins, vec3_t maxs, float epsilon )
\r
642 float highMin, lowMax, volume, bestVolume;
\r
643 vec3_t fogMins, fogMaxs, overlap;
\r
647 /* start with bogus fog num */
\r
648 fogNum = defaultFogNum;
\r
653 /* walk the list of fog volumes */
\r
654 for( i = 0; i < numMapFogs; i++ )
\r
656 /* sof2: global fog doesn't reference a brush */
\r
657 if( mapFogs[ i ].brush == NULL )
\r
663 /* get fog brush */
\r
664 brush = mapFogs[ i ].brush;
\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
674 /* check against bounds */
\r
675 for( j = 0; j < 3; j++ )
\r
677 if( mins[ j ] > fogMaxs[ j ] || maxs[ j ] < fogMins[ j ] )
\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
691 volume = overlap[ 0 ] * overlap[ 1 ] * overlap[ 2 ];
\r
693 /* test against best volume */
\r
694 if( volume > bestVolume )
\r
696 bestVolume = volume;
\r
701 /* if the point made it this far, it's not inside any fog volumes (or inside global fog) */
\r
708 CreateMapFogs() - ydnar
\r
709 generates a list of map fogs
\r
712 void CreateMapFogs( void )
\r
719 const char *globalFog;
\r
727 Sys_FPrintf( SYS_VRB, "--- CreateMapFogs ---\n" );
\r
729 /* walk entities */
\r
730 for( i = 0; i < numEntities; i++ )
\r
733 entity = &entities[ i ];
\r
735 /* walk entity brushes */
\r
736 for( brush = entity->brushes; brush != NULL; brush = brush->next )
\r
738 /* ignore non-fog brushes */
\r
739 if( brush->contentShader->fogParms == qfalse )
\r
743 if( numMapFogs >= MAX_MAP_FOGS )
\r
744 Error( "Exceeded MAX_MAP_FOGS (%d)", MAX_MAP_FOGS );
\r
747 fog = &mapFogs[ numMapFogs++ ];
\r
748 fog->si = brush->contentShader;
\r
749 fog->brush = brush;
\r
750 fog->visibleSide = -1;
\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
756 VectorScale( fog->si->fogDir, -1.0f, invFogDir );
\r
758 /* find the brush side */
\r
759 for( i = 0; i < brush->numsides; i++ )
\r
761 if( VectorCompare( invFogDir, mapplanes[ brush->sides[ i ].planenum ].normal ) )
\r
763 fog->visibleSide = i;
\r
764 //% Sys_Printf( "Brush num: %d Side num: %d\n", fog->brushNum, fog->visibleSide );
\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
779 if( numMapFogs >= MAX_MAP_FOGS )
\r
780 Error( "Exceeded MAX_MAP_FOGS (%d) trying to add global fog", MAX_MAP_FOGS );
\r
783 Sys_FPrintf( SYS_VRB, "Map has global fog shader %s\n", globalFog );
\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
791 fog->visibleSide = -1;
\r
793 /* set as default fog */
\r
794 defaultFogNum = numMapFogs - 1;
\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
801 /* emit some stats */
\r
802 Sys_FPrintf( SYS_VRB, "%9d fogs\n", numMapFogs );
\r