Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

fog.c (12727B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 #include "qbsp.h"
     23 
     24 
     25 int			c_fogFragment;
     26 int			c_fogPatchFragments;
     27 
     28 /*
     29 ====================
     30 DrawSurfToMesh
     31 ====================
     32 */
     33 mesh_t	*DrawSurfToMesh( mapDrawSurface_t *ds ) {
     34 	mesh_t		*m;
     35 
     36 	m = malloc( sizeof( *m ) );
     37 	m->width = ds->patchWidth;
     38 	m->height = ds->patchHeight;
     39 	m->verts = malloc( sizeof(m->verts[0]) * m->width * m->height );
     40 	memcpy( m->verts, ds->verts, sizeof(m->verts[0]) * m->width * m->height );
     41 
     42 	return m;
     43 }
     44 
     45 
     46 /*
     47 ====================
     48 SplitMeshByPlane
     49 ====================
     50 */
     51 void SplitMeshByPlane( mesh_t *in, vec3_t normal, float dist, mesh_t **front, mesh_t **back ) {
     52 	int		w, h, split;
     53 	float	d[MAX_PATCH_SIZE][MAX_PATCH_SIZE];
     54 	drawVert_t	*dv, *v1, *v2;
     55 	int		c_front, c_back, c_on;
     56 	mesh_t	*f, *b;
     57 	int		i;
     58 	float	frac;
     59 	int		frontAprox, backAprox;
     60 
     61 	for ( i = 0 ; i < 2 ; i++ ) {
     62 		dv = in->verts;
     63 		c_front = 0;
     64 		c_back = 0;
     65 		c_on = 0;
     66 		for ( h = 0 ; h < in->height ; h++ ) {
     67 			for ( w = 0 ; w < in->width ; w++, dv++ ) {
     68 				d[h][w] = DotProduct( dv->xyz, normal ) - dist;
     69 				if ( d[h][w] > ON_EPSILON ) {
     70 					c_front++;
     71 				} else if ( d[h][w] < -ON_EPSILON ) {
     72 					c_back++;
     73 				} else {
     74 					c_on++;
     75 				}
     76 			}
     77 		}
     78 
     79 		*front = NULL;
     80 		*back = NULL;
     81 
     82 		if ( !c_front ) {
     83 			*back = in;
     84 			return;
     85 		}
     86 		if ( !c_back ) {
     87 			*front = in;
     88 			return;
     89 		}
     90 
     91 		// find a split point
     92 		split = -1;
     93 		for ( w = 0 ; w < in->width -1 ; w++ ) {
     94 			if ( ( d[0][w] < 0 ) != ( d[0][w+1] < 0 ) ) {
     95 				if ( split == -1 ) {
     96 					split = w;
     97 					break;
     98 				}
     99 			}
    100 		}
    101 
    102 		if ( split == -1 ) {
    103 			if ( i == 1 ) {
    104 				qprintf( "No crossing points in patch\n");
    105 				*front = in;
    106 				return;
    107 			}
    108 
    109 			in = TransposeMesh( in );
    110 			InvertMesh( in );
    111 			continue;
    112 		}
    113 
    114 		// make sure the split point stays the same for all other rows
    115 		for ( h = 1 ; h < in->height ; h++ ) {
    116 			for ( w = 0 ; w < in->width -1 ; w++ ) {
    117 				if ( ( d[h][w] < 0 ) != ( d[h][w+1] < 0 ) ) {
    118 					if ( w != split ) {
    119 						_printf( "multiple crossing points for patch -- can't clip\n");
    120 						*front = in;
    121 						return;
    122 					}
    123 				}
    124 			}
    125 			if ( ( d[h][split] < 0 ) == ( d[h][split+1] < 0 ) ) {
    126 				_printf( "differing crossing points for patch -- can't clip\n");
    127 				*front = in;
    128 				return;
    129 			}
    130 		}
    131 
    132 		break;
    133 	}
    134 
    135 
    136 	// create two new meshes
    137 	f = malloc( sizeof( *f ) );
    138 	f->width = split + 2;
    139 	if ( ! (f->width & 1) ) {
    140 		f->width++;
    141 		frontAprox = 1;
    142 	} else {
    143 		frontAprox = 0;
    144 	}
    145 	if ( f->width > MAX_PATCH_SIZE ) {
    146 		Error( "MAX_PATCH_SIZE after split");
    147 	}
    148 	f->height = in->height;
    149 	f->verts = malloc( sizeof(f->verts[0]) * f->width * f->height );
    150 
    151 	b = malloc( sizeof( *b ) );
    152 	b->width = in->width - split;
    153 	if ( ! (b->width & 1) ) {
    154 		b->width++;
    155 		backAprox = 1;
    156 	} else {
    157 		backAprox = 0;
    158 	}
    159 	if ( b->width > MAX_PATCH_SIZE ) {
    160 		Error( "MAX_PATCH_SIZE after split");
    161 	}
    162 	b->height = in->height;
    163 	b->verts = malloc( sizeof(b->verts[0]) * b->width * b->height );
    164 
    165 	if ( d[0][0] > 0 ) {
    166 		*front = f;
    167 		*back = b;
    168 	} else {
    169 		*front = b;
    170 		*back = f;
    171 	}
    172 
    173 	// distribute the points
    174 	for ( w = 0 ; w < in->width ; w++ ) {
    175 		for ( h = 0 ; h < in->height ; h++ ) {
    176 			if ( w <= split ) {
    177 				f->verts[ h * f->width + w ] = in->verts[ h * in->width + w ];
    178 			} else {
    179 				b->verts[ h * b->width + w - split + backAprox ] = in->verts[ h * in->width + w ];
    180 			}
    181 		}
    182 	}
    183 
    184 	// clip the crossing line
    185 	for ( h = 0 ; h < in->height ; h++ ) {
    186 		dv = &f->verts[ h * f->width + split + 1 ];
    187 		v1 = &in->verts[ h * in->width + split ];
    188 		v2 = &in->verts[ h * in->width + split + 1 ];
    189 		frac = d[h][split] / ( d[h][split] - d[h][split+1] );
    190 		for ( i = 0 ; i < 10 ; i++ ) {
    191 			dv->xyz[i] = v1->xyz[i] + frac * ( v2->xyz[i] - v1->xyz[i] );
    192 		}
    193 		dv->xyz[10] = 0;//set all 4 colors to 0 
    194 		if ( frontAprox ) {
    195 			f->verts[ h * f->width + split + 2 ] = *dv;
    196 		}
    197 		b->verts[ h * b->width ] = *dv;
    198 		if ( backAprox ) {
    199 			b->verts[ h * b->width + 1 ] = *dv;
    200 		}
    201 	}
    202 
    203 	/*
    204 PrintMesh( in );
    205 _printf("\n");
    206 PrintMesh( f );
    207 _printf("\n");
    208 PrintMesh( b );
    209 _printf("\n");
    210 	*/
    211 
    212 	FreeMesh( in );
    213 }
    214 
    215 
    216 /*
    217 ====================
    218 ChopPatchByBrush
    219 ====================
    220 */
    221 qboolean ChopPatchByBrush( mapDrawSurface_t *ds, bspbrush_t *b ) {
    222 	int			i, j;
    223 	side_t		*s;
    224 	plane_t		*plane;
    225 	mesh_t		*outside[MAX_BRUSH_SIDES];
    226 	int			numOutside;
    227 	mesh_t		*m, *front, *back;
    228 	mapDrawSurface_t	*newds;
    229 
    230 	m = DrawSurfToMesh( ds );
    231 	numOutside = 0;
    232 
    233 	// only split by the top and bottom planes to avoid
    234 	// some messy patch clipping issues
    235 
    236 	for ( i = 4 ; i <= 5 ; i++ ) {
    237 		s = &b->sides[ i ];
    238 		plane = &mapplanes[ s->planenum ];
    239 
    240 		SplitMeshByPlane( m, plane->normal, plane->dist, &front, &back );
    241 
    242 		if ( !back ) {
    243 			// nothing actually contained inside
    244 			for ( j = 0 ; j < numOutside ; j++ ) {
    245 				FreeMesh( outside[j] );
    246 			}
    247 			return qfalse;
    248 		}
    249 		m = back;
    250 
    251 		if ( front ) {
    252 			if ( numOutside == MAX_BRUSH_SIDES ) {
    253 				Error( "MAX_BRUSH_SIDES" );
    254 			}
    255 			outside[ numOutside ] = front;
    256 			numOutside++;
    257 		}
    258 	}
    259 
    260 	// all of outside fragments become seperate drawsurfs
    261 	c_fogPatchFragments += numOutside;
    262 	for ( i = 0 ; i < numOutside ; i++ ) {
    263 		newds = DrawSurfaceForMesh( outside[ i ] );
    264 		newds->shaderInfo = ds->shaderInfo;
    265 		FreeMesh( outside[ i ] );
    266 	}
    267 
    268 	// replace ds with m
    269 	ds->patchWidth = m->width;
    270 	ds->patchHeight = m->height;
    271 	ds->numVerts = m->width * m->height;
    272 	free( ds->verts );
    273 	ds->verts = malloc( ds->numVerts * sizeof( *ds->verts ) );
    274 	memcpy( ds->verts, m->verts, ds->numVerts * sizeof( *ds->verts ) );
    275 
    276 	FreeMesh( m );
    277 
    278 	return qtrue;
    279 }
    280 
    281 //===============================================================================
    282 
    283 /*
    284 ====================
    285 WindingFromDrawSurf
    286 ====================
    287 */
    288 winding_t	*WindingFromDrawSurf( mapDrawSurface_t *ds ) {
    289 	winding_t	*w;
    290 	int			i;
    291 
    292 	w = AllocWinding( ds->numVerts );
    293 	w->numpoints = ds->numVerts;
    294 	for ( i = 0 ; i < ds->numVerts ; i++ ) {
    295 		VectorCopy( ds->verts[i].xyz, w->p[i] );
    296 	}
    297 	return w;
    298 }
    299 
    300 /*
    301 ====================
    302 ChopFaceByBrush
    303 
    304 There may be a fragment contained in the brush
    305 ====================
    306 */
    307 qboolean ChopFaceByBrush( mapDrawSurface_t *ds, bspbrush_t *b ) {
    308 	int			i, j;
    309 	side_t		*s;
    310 	plane_t		*plane;
    311 	winding_t	*w;
    312 	winding_t	*front, *back;
    313 	winding_t	*outside[MAX_BRUSH_SIDES];
    314 	int			numOutside;
    315 	mapDrawSurface_t	*newds;
    316 	drawVert_t		*dv;
    317 	shaderInfo_t	*si;
    318 	float		mins[2];
    319 
    320 	// brush primitive :
    321 	// axis base
    322 	vec3_t		texX,texY;
    323 	vec_t		x,y;
    324 
    325 	w = WindingFromDrawSurf( ds );
    326 	numOutside = 0;
    327 
    328 	for ( i = 0 ; i < b->numsides ; i++ ) {
    329 		s = &b->sides[ i ];
    330 		if ( s->backSide ) {
    331 			continue;
    332 		}
    333 		plane = &mapplanes[ s->planenum ];
    334 
    335 		// handle coplanar outfacing (don't fog)
    336 		if ( ds->side->planenum == s->planenum ) {
    337 			return qfalse;
    338 		}
    339 
    340 		// handle coplanar infacing (keep inside)
    341 		if ( ( ds->side->planenum ^ 1 ) == s->planenum ) {
    342 			continue;
    343 		}
    344 
    345 		// general case
    346 		ClipWindingEpsilon( w, plane->normal, plane->dist, ON_EPSILON,
    347 			&front, &back );
    348 		FreeWinding( w );
    349 		if ( !back ) {
    350 			// nothing actually contained inside
    351 			for ( j = 0 ; j < numOutside ; j++ ) {
    352 				FreeWinding( outside[j] );
    353 			}
    354 			return qfalse;
    355 		}
    356 		if ( front ) {
    357 			if ( numOutside == MAX_BRUSH_SIDES ) {
    358 				Error( "MAX_BRUSH_SIDES" );
    359 			}
    360 			outside[ numOutside ] = front;
    361 			numOutside++;
    362 		}
    363 		w = back;
    364 	}
    365 
    366 	// all of outside fragments become seperate drawsurfs
    367 	// linked to the same side
    368 	c_fogFragment += numOutside;
    369 	s = ds->side;
    370 
    371 	for ( i = 0 ; i < numOutside ; i++ ) {
    372 		newds = DrawSurfaceForSide( ds->mapBrush, s, outside[i] );
    373 		FreeWinding( outside[i] );
    374 	}
    375 
    376 
    377 	// replace ds->verts with the verts for w
    378 	ds->numVerts = w->numpoints;
    379 	free( ds->verts );
    380 
    381 	ds->verts = malloc( ds->numVerts * sizeof( *ds->verts ) );
    382 	memset( ds->verts, 0, ds->numVerts * sizeof( *ds->verts ) );
    383 
    384 	si = s->shaderInfo;
    385 
    386 	mins[0] = 9999;
    387 	mins[1] = 9999;
    388 
    389 	// compute s/t coordinates from brush primitive texture matrix
    390 	// compute axis base
    391 	ComputeAxisBase( mapplanes[s->planenum].normal, texX, texY );
    392 
    393 	for ( j = 0 ; j < w->numpoints ; j++ ) {
    394 		dv = ds->verts + j;
    395 		VectorCopy( w->p[j], dv->xyz );
    396 	
    397 		if (g_bBrushPrimit==BPRIMIT_OLDBRUSHES)
    398 		{
    399 			// calculate texture s/t
    400 			dv->st[0] = s->vecs[0][3] + DotProduct( s->vecs[0],	dv->xyz );
    401 			dv->st[1] = s->vecs[1][3] + DotProduct( s->vecs[1],	dv->xyz );	
    402 			dv->st[0] /= si->width;
    403 			dv->st[1] /= si->height;
    404 		}
    405 		else
    406 		{
    407 			// calculate texture s/t from brush primitive texture matrix
    408 			x = DotProduct( dv->xyz, texX );
    409 			y = DotProduct( dv->xyz, texY );
    410 			dv->st[0]=s->texMat[0][0]*x+s->texMat[0][1]*y+s->texMat[0][2];
    411 			dv->st[1]=s->texMat[1][0]*x+s->texMat[1][1]*y+s->texMat[1][2];
    412 		}
    413 
    414 		if ( dv->st[0] < mins[0] ) {
    415 			mins[0] = dv->st[0];
    416 		}
    417 		if ( dv->st[1] < mins[1] ) {
    418 			mins[1] = dv->st[1];
    419 		}
    420 
    421 		// copy normal
    422 		VectorCopy ( mapplanes[s->planenum].normal, dv->normal );
    423 	}
    424 
    425 	// adjust the texture coordinates to be as close to 0 as possible
    426 	if ( !si->globalTexture ) {
    427 		mins[0] = floor( mins[0] );
    428 		mins[1] = floor( mins[1] );
    429 		for ( i = 0 ; i < w->numpoints ; i++ ) {
    430 			dv = ds->verts + i;
    431 			dv->st[0] -= mins[0];
    432 			dv->st[1] -= mins[1];
    433 		}
    434 	}
    435 
    436 	return qtrue;
    437 }
    438 
    439 //===============================================================================
    440 
    441 
    442 /*
    443 =====================
    444 FogDrawSurfs
    445 
    446 Call after the surface list has been pruned, 
    447 before tjunction fixing
    448 before lightmap allocation
    449 =====================
    450 */
    451 void FogDrawSurfs( void ) {
    452 	int					i, j, k;
    453 	mapDrawSurface_t	*ds;
    454 	bspbrush_t			*b;
    455 	vec3_t				mins, maxs;
    456 	int					c_fogged;
    457 	int					numBaseDrawSurfs;
    458 	dfog_t				*fog;
    459 
    460 	qprintf("----- FogDrawsurfs -----\n");
    461 
    462 	c_fogged = 0;
    463 	c_fogFragment = 0;
    464 
    465 	// find all fog brushes
    466 	for ( b = entities[0].brushes ; b ; b = b->next ) {
    467 		if ( !(b->contents & CONTENTS_FOG) ) {
    468 			continue;
    469 		}
    470 
    471 		if ( numFogs == MAX_MAP_FOGS ) {
    472 			Error( "MAX_MAP_FOGS" );
    473 		}
    474 		fog = &dfogs[numFogs];
    475 		numFogs++;
    476 		fog->brushNum = b->outputNumber;
    477 
    478 		// find a side with a valid shaderInfo
    479 		// non-axial fog columns may have bevel planes that need to be skipped
    480 		for ( i = 0 ; i < b->numsides ; i++ ) {
    481 			if ( b->sides[i].shaderInfo && (b->sides[i].shaderInfo->contents & CONTENTS_FOG) ) {
    482 				strcpy( fog->shader, b->sides[i].shaderInfo->shader );
    483 				break;
    484 			}
    485 		}
    486 		if ( i == b->numsides ) {
    487 			continue;		// shouldn't happen
    488 		}
    489 
    490 		fog->visibleSide = -1;
    491 
    492 		// clip each surface into this, but don't clip any of
    493 		// the resulting fragments to the same brush
    494 		numBaseDrawSurfs = numMapDrawSurfs;
    495 		for ( i = 0 ; i < numBaseDrawSurfs ; i++ ) {
    496 			ds = &mapDrawSurfs[i];
    497 
    498 			// bound the drawsurf
    499 			ClearBounds( mins, maxs );
    500 			for ( j = 0 ; j < ds->numVerts ; j++ ) {
    501 				AddPointToBounds( ds->verts[j].xyz, mins, maxs );
    502 			}
    503 
    504 			// check against the fog brush
    505 			for ( k = 0 ; k < 3 ; k++ ) {
    506 				if ( mins[k] > b->maxs[k] ) {
    507 					break;
    508 				}
    509 				if ( maxs[k] < b->mins[k] ) {
    510 					break;
    511 				}
    512 			}
    513 			if ( k < 3 ) {
    514 				continue;		// bboxes don't intersect
    515 			}
    516 
    517 			if ( ds->mapBrush == b ) {
    518 				int		s;
    519 
    520 				s = ds->side - b->sides;
    521 				if ( s <= 6 ) {	// not one of the reversed inside faces
    522 					// this is a visible fog plane
    523 					if ( fog->visibleSide != -1 ) {
    524 						_printf( "WARNING: fog brush %i has multiple visible sides\n", b->brushnum );
    525 					}
    526 					fog->visibleSide = s;
    527 				}
    528 			}
    529 
    530 			if ( ds->miscModel ) {
    531 				// we could write splitting code for trimodels if we wanted to...
    532 				c_fogged++;
    533 				ds->fogNum = numFogs - 1;
    534 			} else if ( ds->patch ) {
    535 				if ( ChopPatchByBrush( ds, b ) ) {
    536 					c_fogged++;
    537 					ds->fogNum = numFogs - 1;
    538 				}
    539 			} else {
    540 				if ( ChopFaceByBrush( ds, b ) ) {
    541 					c_fogged++;
    542 					ds->fogNum = numFogs - 1;
    543 				}
    544 			}
    545 		}
    546 	}
    547 
    548 	// split the drawsurfs by the fog brushes
    549 
    550 	qprintf( "%5i fogs\n", numFogs );
    551 	qprintf( "%5i fog polygon fragments\n", c_fogFragment );
    552 	qprintf( "%5i fog patch fragments\n", c_fogPatchFragments );
    553 	qprintf( "%5i fogged drawsurfs\n", c_fogged );
    554 }