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 }