tr_frontend_deform.cpp (34314B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. 23 24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. 25 26 =========================================================================== 27 */ 28 29 #pragma hdrstop 30 #include "../idlib/precompiled.h" 31 32 #include "tr_local.h" 33 #include "Model_local.h" 34 35 /* 36 ========================================================================================== 37 38 DEFORM SURFACES 39 40 ========================================================================================== 41 */ 42 43 /* 44 ================= 45 R_FinishDeform 46 ================= 47 */ 48 static drawSurf_t * R_FinishDeform( drawSurf_t * surf, srfTriangles_t * newTri, const idDrawVert * newVerts, const triIndex_t * newIndexes ) { 49 newTri->ambientCache = vertexCache.AllocVertex( newVerts, ALIGN( newTri->numVerts * sizeof( idDrawVert ), VERTEX_CACHE_ALIGN ) ); 50 newTri->indexCache = vertexCache.AllocIndex( newIndexes, ALIGN( newTri->numIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) ); 51 52 surf->frontEndGeo = newTri; 53 surf->numIndexes = newTri->numIndexes; 54 surf->ambientCache = newTri->ambientCache; 55 surf->indexCache = newTri->indexCache; 56 surf->shadowCache = 0; 57 surf->jointCache = 0; 58 surf->nextOnLight = NULL; 59 60 return surf; 61 } 62 63 /* 64 ===================== 65 R_AutospriteDeform 66 67 Assuming all the triangles for this shader are independant 68 quads, rebuild them as forward facing sprites. 69 ===================== 70 */ 71 static drawSurf_t * R_AutospriteDeform( drawSurf_t *surf ) { 72 const srfTriangles_t * srcTri = surf->frontEndGeo; 73 74 if ( srcTri->numVerts & 3 ) { 75 common->Warning( "R_AutospriteDeform: shader had odd vertex count" ); 76 return NULL; 77 } 78 if ( srcTri->numIndexes != ( srcTri->numVerts >> 2 ) * 6 ) { 79 common->Warning( "R_AutospriteDeform: autosprite had odd index count" ); 80 return NULL; 81 } 82 83 const idJointMat * joints = ( srcTri->staticModelWithJoints != NULL && r_useGPUSkinning.GetBool() ) ? srcTri->staticModelWithJoints->jointsInverted : NULL; 84 85 idVec3 leftDir; 86 idVec3 upDir; 87 R_GlobalVectorToLocal( surf->space->modelMatrix, tr.viewDef->renderView.viewaxis[1], leftDir ); 88 R_GlobalVectorToLocal( surf->space->modelMatrix, tr.viewDef->renderView.viewaxis[2], upDir ); 89 90 if ( tr.viewDef->isMirror ) { 91 leftDir = vec3_origin - leftDir; 92 } 93 94 // the srfTriangles_t are in frame memory and will be automatically disposed of 95 srfTriangles_t * newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES ); 96 newTri->numVerts = srcTri->numVerts; 97 newTri->numIndexes = srcTri->numIndexes; 98 99 idDrawVert * newVerts = (idDrawVert *)_alloca16( ALIGN( srcTri->numVerts * sizeof( idDrawVert ), 16 ) ); 100 triIndex_t * newIndexes = (triIndex_t *)_alloca16( ALIGN( srcTri->numIndexes * sizeof( triIndex_t ), 16 ) ); 101 102 for ( int i = 0; i < srcTri->numVerts; i += 4 ) { 103 // find the midpoint 104 newVerts[i+0] = idDrawVert::GetSkinnedDrawVert( srcTri->verts[i + 0], joints ); 105 newVerts[i+1] = idDrawVert::GetSkinnedDrawVert( srcTri->verts[i + 1], joints ); 106 newVerts[i+2] = idDrawVert::GetSkinnedDrawVert( srcTri->verts[i + 2], joints ); 107 newVerts[i+3] = idDrawVert::GetSkinnedDrawVert( srcTri->verts[i + 3], joints ); 108 109 idVec3 mid; 110 mid[0] = 0.25f * ( newVerts[i+0].xyz[0] + newVerts[i+1].xyz[0] + newVerts[i+2].xyz[0] + newVerts[i+3].xyz[0] ); 111 mid[1] = 0.25f * ( newVerts[i+0].xyz[1] + newVerts[i+1].xyz[1] + newVerts[i+2].xyz[1] + newVerts[i+3].xyz[1] ); 112 mid[2] = 0.25f * ( newVerts[i+0].xyz[2] + newVerts[i+1].xyz[2] + newVerts[i+2].xyz[2] + newVerts[i+3].xyz[2] ); 113 114 const idVec3 delta = newVerts[i+0].xyz - mid; 115 const float radius = delta.Length() * idMath::SQRT_1OVER2; 116 117 const idVec3 left = leftDir * radius; 118 const idVec3 up = upDir * radius; 119 120 newVerts[i+0].xyz = mid + left + up; 121 newVerts[i+0].SetTexCoord( 0, 0 ); 122 newVerts[i+1].xyz = mid - left + up; 123 newVerts[i+1].SetTexCoord( 1, 0 ); 124 newVerts[i+2].xyz = mid - left - up; 125 newVerts[i+2].SetTexCoord( 1, 1 ); 126 newVerts[i+3].xyz = mid + left - up; 127 newVerts[i+3].SetTexCoord( 0, 1 ); 128 129 newIndexes[6*(i>>2)+0] = i+0; 130 newIndexes[6*(i>>2)+1] = i+1; 131 newIndexes[6*(i>>2)+2] = i+2; 132 133 newIndexes[6*(i>>2)+3] = i+0; 134 newIndexes[6*(i>>2)+4] = i+2; 135 newIndexes[6*(i>>2)+5] = i+3; 136 } 137 138 return R_FinishDeform( surf, newTri, newVerts, newIndexes ); 139 } 140 141 /* 142 ===================== 143 R_TubeDeform 144 145 Will pivot a rectangular quad along the center of its long axis. 146 147 Note that a geometric tube with even quite a few sides will almost certainly render 148 much faster than this, so this should only be for faked volumetric tubes. 149 Make sure this is used with twosided translucent shaders, because the exact side 150 order may not be correct. 151 ===================== 152 */ 153 static drawSurf_t * R_TubeDeform( drawSurf_t * surf ) { 154 static int edgeVerts[6][2] = { 155 { 0, 1 }, 156 { 1, 2 }, 157 { 2, 0 }, 158 { 3, 4 }, 159 { 4, 5 }, 160 { 5, 3 } 161 }; 162 163 const srfTriangles_t * srcTri = surf->frontEndGeo; 164 165 if ( srcTri->numVerts & 3 ) { 166 common->Error( "R_TubeDeform: shader had odd vertex count" ); 167 } 168 if ( srcTri->numIndexes != ( srcTri->numVerts >> 2 ) * 6 ) { 169 common->Error( "R_TubeDeform: autosprite had odd index count" ); 170 } 171 172 const idJointMat * joints = ( srcTri->staticModelWithJoints != NULL && r_useGPUSkinning.GetBool() ) ? srcTri->staticModelWithJoints->jointsInverted : NULL; 173 174 // we need the view direction to project the minor axis of the tube 175 // as the view changes 176 idVec3 localView; 177 R_GlobalPointToLocal( surf->space->modelMatrix, tr.viewDef->renderView.vieworg, localView ); 178 179 // the srfTriangles_t are in frame memory and will be automatically disposed of 180 srfTriangles_t * newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES ); 181 newTri->numVerts = srcTri->numVerts; 182 newTri->numIndexes = srcTri->numIndexes; 183 184 idDrawVert * newVerts = (idDrawVert *)_alloca16( ALIGN( srcTri->numVerts * sizeof( idDrawVert ), 16 ) ); 185 for ( int i = 0; i < srcTri->numVerts; i++ ) { 186 newVerts[i].Clear(); 187 } 188 189 // this is a lot of work for two triangles... 190 // we could precalculate a lot if it is an issue, but it would mess up the shader abstraction 191 for ( int i = 0, indexes = 0; i < srcTri->numVerts; i += 4, indexes += 6 ) { 192 // identify the two shortest edges out of the six defined by the indexes 193 int nums[2] = { 0, 0 }; 194 float lengths[2] = { 999999.0f, 999999.0f }; 195 196 for ( int j = 0; j < 6; j++ ) { 197 const idVec3 v1 = idDrawVert::GetSkinnedDrawVertPosition( srcTri->verts[srcTri->indexes[i + edgeVerts[j][0]]], joints ); 198 const idVec3 v2 = idDrawVert::GetSkinnedDrawVertPosition( srcTri->verts[srcTri->indexes[i + edgeVerts[j][1]]], joints ); 199 200 const float l = ( v1 - v2 ).Length(); 201 if ( l < lengths[0] ) { 202 nums[1] = nums[0]; 203 lengths[1] = lengths[0]; 204 nums[0] = j; 205 lengths[0] = l; 206 } else if ( l < lengths[1] ) { 207 nums[1] = j; 208 lengths[1] = l; 209 } 210 } 211 212 // find the midpoints of the two short edges, which 213 // will give us the major axis in object coordinates 214 idVec3 mid[2]; 215 for ( int j = 0; j < 2; j++ ) { 216 const idVec3 v1 = idDrawVert::GetSkinnedDrawVertPosition( srcTri->verts[srcTri->indexes[i+edgeVerts[nums[j]][0]]], joints ); 217 const idVec3 v2 = idDrawVert::GetSkinnedDrawVertPosition( srcTri->verts[srcTri->indexes[i+edgeVerts[nums[j]][1]]], joints ); 218 219 mid[j][0] = 0.5f * ( v1[0] + v2[0] ); 220 mid[j][1] = 0.5f * ( v1[1] + v2[1] ); 221 mid[j][2] = 0.5f * ( v1[2] + v2[2] ); 222 } 223 224 // find the vector of the major axis 225 const idVec3 major = mid[1] - mid[0]; 226 227 // re-project the points 228 for ( int j = 0; j < 2; j++ ) { 229 const int i1 = srcTri->indexes[i+edgeVerts[nums[j]][0]]; 230 const int i2 = srcTri->indexes[i+edgeVerts[nums[j]][1]]; 231 232 newVerts[i1] = idDrawVert::GetSkinnedDrawVert( srcTri->verts[i1], joints ); 233 newVerts[i2] = idDrawVert::GetSkinnedDrawVert( srcTri->verts[i2], joints ); 234 235 const float l = 0.5f * lengths[j]; 236 237 // cross this with the view direction to get minor axis 238 idVec3 dir = mid[j] - localView; 239 idVec3 minor; 240 minor.Cross( major, dir ); 241 minor.Normalize(); 242 243 if ( j ) { 244 newVerts[i1].xyz = mid[j] - l * minor; 245 newVerts[i2].xyz = mid[j] + l * minor; 246 } else { 247 newVerts[i1].xyz = mid[j] + l * minor; 248 newVerts[i2].xyz = mid[j] - l * minor; 249 } 250 } 251 } 252 253 return R_FinishDeform( surf, newTri, newVerts, srcTri->indexes ); 254 } 255 256 /* 257 ===================== 258 R_WindingFromTriangles 259 ===================== 260 */ 261 #define MAX_TRI_WINDING_INDEXES 16 262 int R_WindingFromTriangles( const srfTriangles_t *tri, triIndex_t indexes[MAX_TRI_WINDING_INDEXES] ) { 263 int i, j, k, l; 264 265 indexes[0] = tri->indexes[0]; 266 int numIndexes = 1; 267 int numTris = tri->numIndexes / 3; 268 269 do { 270 // find an edge that goes from the current index to another 271 // index that isn't already used, and isn't an internal edge 272 for ( i = 0; i < numTris; i++ ) { 273 for ( j = 0; j < 3; j++ ) { 274 if ( tri->indexes[i*3+j] != indexes[numIndexes-1] ) { 275 continue; 276 } 277 int next = tri->indexes[i*3+(j+1)%3]; 278 279 // make sure it isn't already used 280 if ( numIndexes == 1 ) { 281 if ( next == indexes[0] ) { 282 continue; 283 } 284 } else { 285 for ( k = 1; k < numIndexes; k++ ) { 286 if ( indexes[k] == next ) { 287 break; 288 } 289 } 290 if ( k != numIndexes ) { 291 continue; 292 } 293 } 294 295 // make sure it isn't an interior edge 296 for ( k = 0; k < numTris; k++ ) { 297 if ( k == i ) { 298 continue; 299 } 300 for ( l = 0; l < 3; l++ ) { 301 int a, b; 302 303 a = tri->indexes[k*3+l]; 304 if ( a != next ) { 305 continue; 306 } 307 b = tri->indexes[k*3+(l+1)%3]; 308 if ( b != indexes[numIndexes-1] ) { 309 continue; 310 } 311 312 // this is an interior edge 313 break; 314 } 315 if ( l != 3 ) { 316 break; 317 } 318 } 319 if ( k != numTris ) { 320 continue; 321 } 322 323 // add this to the list 324 indexes[numIndexes] = next; 325 numIndexes++; 326 break; 327 } 328 if ( j != 3 ) { 329 break; 330 } 331 } 332 if ( numIndexes == tri->numVerts ) { 333 break; 334 } 335 } while ( i != numTris ); 336 337 return numIndexes; 338 } 339 340 /* 341 ===================== 342 R_FlareDeform 343 ===================== 344 */ 345 static drawSurf_t * R_FlareDeform( drawSurf_t * surf ) { 346 const srfTriangles_t * srcTri = surf->frontEndGeo; 347 348 assert( srcTri->staticModelWithJoints == NULL ); 349 350 if ( srcTri->numVerts != 4 || srcTri->numIndexes != 6 ) { 351 // FIXME: temp hack for flares on tripleted models 352 common->Warning( "R_FlareDeform: not a single quad" ); 353 return NULL; 354 } 355 356 // find the plane 357 idPlane plane; 358 plane.FromPoints( srcTri->verts[srcTri->indexes[0]].xyz, srcTri->verts[srcTri->indexes[1]].xyz, srcTri->verts[srcTri->indexes[2]].xyz ); 359 360 // if viewer is behind the plane, draw nothing 361 idVec3 localViewer; 362 R_GlobalPointToLocal( surf->space->modelMatrix, tr.viewDef->renderView.vieworg, localViewer ); 363 float distFromPlane = localViewer * plane.Normal() + plane[3]; 364 if ( distFromPlane <= 0 ) { 365 return NULL; 366 } 367 368 idVec3 center = srcTri->verts[0].xyz; 369 for ( int i = 1; i < srcTri->numVerts; i++ ) { 370 center += srcTri->verts[i].xyz; 371 } 372 center *= 1.0f / srcTri->numVerts; 373 374 idVec3 dir = localViewer - center; 375 dir.Normalize(); 376 377 const float dot = dir * plane.Normal(); 378 379 // set vertex colors based on plane angle 380 int color = idMath::Ftoi( dot * 8 * 256 ); 381 if ( color > 255 ) { 382 color = 255; 383 } 384 385 triIndex_t indexes[MAX_TRI_WINDING_INDEXES]; 386 int numIndexes = R_WindingFromTriangles( srcTri, indexes ); 387 388 // only deal with quads 389 if ( numIndexes != 4 ) { 390 return NULL; 391 } 392 393 const int maxVerts = 16; 394 const int maxIndexes = 18 * 3; 395 396 // the srfTriangles_t are in frame memory and will be automatically disposed of 397 srfTriangles_t * newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES ); 398 newTri->numVerts = maxVerts; 399 newTri->numIndexes = maxIndexes; 400 401 idDrawVert *newVerts = (idDrawVert *)_alloca16( ALIGN( maxVerts * sizeof( idDrawVert ), 16 ) ); 402 403 idVec3 edgeDir[4][3]; 404 405 // calculate vector directions 406 for ( int i = 0; i < 4; i++ ) { 407 newVerts[i].Clear(); 408 newVerts[i].xyz = srcTri->verts[ indexes[i] ].xyz; 409 newVerts[i].SetTexCoord( 0.5f, 0.5f ); 410 newVerts[i].color[0] = color; 411 newVerts[i].color[1] = color; 412 newVerts[i].color[2] = color; 413 newVerts[i].color[3] = 255; 414 415 idVec3 toEye = srcTri->verts[ indexes[i] ].xyz - localViewer; 416 toEye.Normalize(); 417 418 idVec3 d1 = srcTri->verts[ indexes[(i+1)%4] ].xyz - localViewer; 419 d1.Normalize(); 420 edgeDir[i][2].Cross( toEye, d1 ); 421 edgeDir[i][2].Normalize(); 422 edgeDir[i][2] = vec3_origin - edgeDir[i][2]; 423 424 idVec3 d2 = srcTri->verts[ indexes[(i+3)%4] ].xyz - localViewer; 425 d2.Normalize(); 426 edgeDir[i][0].Cross( toEye, d2 ); 427 edgeDir[i][0].Normalize(); 428 429 edgeDir[i][1] = edgeDir[i][0] + edgeDir[i][2]; 430 edgeDir[i][1].Normalize(); 431 } 432 433 const float spread = surf->shaderRegisters[ surf->material->GetDeformRegister(0) ] * r_flareSize.GetFloat(); 434 435 for ( int i = 4; i < 16; i++ ) { 436 const int index = ( i - 4 ) / 3; 437 idVec3 v = srcTri->verts[indexes[index]].xyz + spread * edgeDir[index][( i - 4 ) % 3]; 438 439 idVec3 dir = v - localViewer; 440 const float len = dir.Normalize(); 441 const float ang = dir * plane.Normal(); 442 const float newLen = -( distFromPlane / ang ); 443 if ( newLen > 0.0f && newLen < len ) { 444 v = localViewer + dir * newLen; 445 } 446 447 newVerts[i].Clear(); 448 newVerts[i].xyz = v; 449 newVerts[i].SetTexCoord( 0.0f, 0.5f ); 450 newVerts[i].color[0] = color; 451 newVerts[i].color[1] = color; 452 newVerts[i].color[2] = color; 453 newVerts[i].color[3] = 255; 454 } 455 456 ALIGNTYPE16 static triIndex_t triIndexes[18*3 + 10] = { 457 0, 4,5, 0,5, 6, 0,6,7, 0,7, 1, 1,7, 8, 1,8, 9, 458 15, 4,0, 15,0, 3, 3,0,1, 3,1, 2, 2,1, 9, 2,9,10, 459 14,15,3, 14,3,13, 13,3,2, 13,2,12, 12,2,11, 11,2,10, 460 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // to make this a multiple of 16 bytes 461 }; 462 463 return R_FinishDeform( surf, newTri, newVerts, triIndexes ); 464 } 465 466 /* 467 ===================== 468 R_ExpandDeform 469 470 Expands the surface along it's normals by a shader amount 471 ===================== 472 */ 473 static drawSurf_t * R_ExpandDeform( drawSurf_t * surf ) { 474 const srfTriangles_t * srcTri = surf->frontEndGeo; 475 476 assert( srcTri->staticModelWithJoints == NULL ); 477 478 // the srfTriangles_t are in frame memory and will be automatically disposed of 479 srfTriangles_t * newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES ); 480 newTri->numVerts = srcTri->numVerts; 481 newTri->numIndexes = srcTri->numIndexes; 482 483 idDrawVert *newVerts = (idDrawVert *)_alloca16( ALIGN( newTri->numVerts * sizeof( idDrawVert ), 16 ) ); 484 485 const float dist = surf->shaderRegisters[ surf->material->GetDeformRegister(0) ]; 486 for ( int i = 0; i < srcTri->numVerts; i++ ) { 487 newVerts[i] = srcTri->verts[i]; 488 newVerts[i].xyz = srcTri->verts[i].xyz + srcTri->verts[i].GetNormal() * dist; 489 } 490 491 return R_FinishDeform( surf, newTri, newVerts, srcTri->indexes ); 492 } 493 494 /* 495 ===================== 496 R_MoveDeform 497 498 Moves the surface along the X axis, mostly just for demoing the deforms 499 ===================== 500 */ 501 static drawSurf_t * R_MoveDeform( drawSurf_t * surf ) { 502 const srfTriangles_t * srcTri = surf->frontEndGeo; 503 504 assert( srcTri->staticModelWithJoints == NULL ); 505 506 // the srfTriangles_t are in frame memory and will be automatically disposed of 507 srfTriangles_t * newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES ); 508 newTri->numVerts = srcTri->numVerts; 509 newTri->numIndexes = srcTri->numIndexes; 510 511 idDrawVert *newVerts = (idDrawVert *)_alloca16( ALIGN( newTri->numVerts * sizeof( idDrawVert ), 16 ) ); 512 513 const float dist = surf->shaderRegisters[ surf->material->GetDeformRegister(0) ]; 514 for ( int i = 0; i < srcTri->numVerts; i++ ) { 515 newVerts[i] = srcTri->verts[i]; 516 newVerts[i].xyz[0] += dist; 517 } 518 519 return R_FinishDeform( surf, newTri, newVerts, srcTri->indexes ); 520 } 521 522 /* 523 ===================== 524 R_TurbulentDeform 525 526 Turbulently deforms the texture coordinates. 527 ===================== 528 */ 529 static drawSurf_t * R_TurbulentDeform( drawSurf_t * surf ) { 530 const srfTriangles_t * srcTri = surf->frontEndGeo; 531 532 assert( srcTri->staticModelWithJoints == NULL ); 533 534 // the srfTriangles_t are in frame memory and will be automatically disposed of 535 srfTriangles_t * newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES ); 536 newTri->numVerts = srcTri->numVerts; 537 newTri->numIndexes = srcTri->numIndexes; 538 539 idDrawVert *newVerts = (idDrawVert *)_alloca16( ALIGN( newTri->numVerts * sizeof( idDrawVert ), 16 ) ); 540 541 const idDeclTable * table = (const idDeclTable *)surf->material->GetDeformDecl(); 542 const float range = surf->shaderRegisters[ surf->material->GetDeformRegister(0) ]; 543 const float timeOfs = surf->shaderRegisters[ surf->material->GetDeformRegister(1) ]; 544 const float domain = surf->shaderRegisters[ surf->material->GetDeformRegister(2) ]; 545 const float tOfs = 0.5f; 546 547 for ( int i = 0; i < srcTri->numVerts; i++ ) { 548 float f = srcTri->verts[i].xyz[0] * 0.003f + srcTri->verts[i].xyz[1] * 0.007f + srcTri->verts[i].xyz[2] * 0.011f; 549 550 f = timeOfs + domain * f; 551 f += timeOfs; 552 553 idVec2 tempST = srcTri->verts[i].GetTexCoord(); 554 tempST[0] += range * table->TableLookup( f ); 555 tempST[1] += range * table->TableLookup( f + tOfs ); 556 557 newVerts[i] = srcTri->verts[i]; 558 newVerts[i].SetTexCoord( tempST ); 559 } 560 561 return R_FinishDeform( surf, newTri, newVerts, srcTri->indexes ); 562 } 563 564 /* 565 ===================== 566 AddTriangleToIsland_r 567 ===================== 568 */ 569 #define MAX_EYEBALL_TRIS 10 570 #define MAX_EYEBALL_ISLANDS 6 571 572 typedef struct { 573 int tris[MAX_EYEBALL_TRIS]; 574 int numTris; 575 idBounds bounds; 576 idVec3 mid; 577 } eyeIsland_t; 578 579 static void AddTriangleToIsland_r( const srfTriangles_t *tri, int triangleNum, bool *usedList, eyeIsland_t *island ) { 580 usedList[triangleNum] = true; 581 582 // add to the current island 583 if ( island->numTris >= MAX_EYEBALL_TRIS ) { 584 common->Error( "MAX_EYEBALL_TRIS" ); 585 return; 586 } 587 island->tris[island->numTris] = triangleNum; 588 island->numTris++; 589 590 const idJointMat * joints = ( tri->staticModelWithJoints != NULL && r_useGPUSkinning.GetBool() ) ? tri->staticModelWithJoints->jointsInverted : NULL; 591 592 // recurse into all neighbors 593 const int a = tri->indexes[triangleNum*3+0]; 594 const int b = tri->indexes[triangleNum*3+1]; 595 const int c = tri->indexes[triangleNum*3+2]; 596 597 const idVec3 va = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[a], joints ); 598 const idVec3 vb = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[b], joints ); 599 const idVec3 vc = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[c], joints ); 600 601 island->bounds.AddPoint( va ); 602 island->bounds.AddPoint( vb ); 603 island->bounds.AddPoint( vc ); 604 605 int numTri = tri->numIndexes / 3; 606 for ( int i = 0; i < numTri; i++ ) { 607 if ( usedList[i] ) { 608 continue; 609 } 610 if ( tri->indexes[i*3+0] == a 611 || tri->indexes[i*3+1] == a 612 || tri->indexes[i*3+2] == a 613 || tri->indexes[i*3+0] == b 614 || tri->indexes[i*3+1] == b 615 || tri->indexes[i*3+2] == b 616 || tri->indexes[i*3+0] == c 617 || tri->indexes[i*3+1] == c 618 || tri->indexes[i*3+2] == c ) { 619 AddTriangleToIsland_r( tri, i, usedList, island ); 620 } 621 } 622 } 623 624 /* 625 ===================== 626 R_EyeballDeform 627 628 Each eyeball surface should have an separate upright triangle behind it, long end 629 pointing out the eye, and another single triangle in front of the eye for the focus point. 630 ===================== 631 */ 632 static drawSurf_t * R_EyeballDeform( drawSurf_t * surf ) { 633 const srfTriangles_t * srcTri = surf->frontEndGeo; 634 635 // separate all the triangles into islands 636 const int numTri = srcTri->numIndexes / 3; 637 if ( numTri > MAX_EYEBALL_ISLANDS * MAX_EYEBALL_TRIS ) { 638 common->Printf( "R_EyeballDeform: too many triangles in surface" ); 639 return NULL; 640 } 641 642 eyeIsland_t islands[MAX_EYEBALL_ISLANDS]; 643 bool triUsed[MAX_EYEBALL_ISLANDS*MAX_EYEBALL_TRIS]; 644 memset( triUsed, 0, sizeof( triUsed ) ); 645 646 int numIslands = 0; 647 for ( ; numIslands < MAX_EYEBALL_ISLANDS; numIslands++ ) { 648 islands[numIslands].numTris = 0; 649 islands[numIslands].bounds.Clear(); 650 int i; 651 for ( i = 0; i < numTri; i++ ) { 652 if ( !triUsed[i] ) { 653 AddTriangleToIsland_r( srcTri, i, triUsed, &islands[numIslands] ); 654 break; 655 } 656 } 657 if ( i == numTri ) { 658 break; 659 } 660 } 661 662 // assume we always have two eyes, two origins, and two targets 663 if ( numIslands != 3 ) { 664 common->Printf( "R_EyeballDeform: %i triangle islands\n", numIslands ); 665 return NULL; 666 } 667 668 const idJointMat * joints = ( srcTri->staticModelWithJoints != NULL && r_useGPUSkinning.GetBool() ) ? srcTri->staticModelWithJoints->jointsInverted : NULL; 669 670 // the srfTriangles_t are in frame memory and will be automatically disposed of 671 srfTriangles_t * newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES ); 672 newTri->numVerts = srcTri->numVerts; 673 newTri->numIndexes = srcTri->numIndexes; 674 675 idDrawVert *newVerts = (idDrawVert *)_alloca16( ALIGN( srcTri->numVerts * sizeof( idDrawVert ), 16 ) ); 676 triIndex_t *newIndexes = (triIndex_t *)_alloca16( ALIGN( srcTri->numIndexes * sizeof( triIndex_t ), 16 ) ); 677 678 // decide which islands are the eyes and points 679 for ( int i = 0; i < numIslands; i++ ) { 680 islands[i].mid = islands[i].bounds.GetCenter(); 681 } 682 683 int numIndexes = 0; 684 for ( int i = 0; i < numIslands; i++ ) { 685 eyeIsland_t * island = &islands[i]; 686 687 if ( island->numTris == 1 ) { 688 continue; 689 } 690 691 // the closest single triangle point will be the eye origin 692 // and the next-to-farthest will be the focal point 693 idVec3 origin; 694 idVec3 focus; 695 int originIsland = 0; 696 float dist[MAX_EYEBALL_ISLANDS]; 697 int sortOrder[MAX_EYEBALL_ISLANDS]; 698 699 for ( int j = 0; j < numIslands; j++ ) { 700 idVec3 dir = islands[j].mid - island->mid; 701 dist[j] = dir.Length(); 702 sortOrder[j] = j; 703 for ( int k = j - 1; k >= 0; k-- ) { 704 if ( dist[k] > dist[k+1] ) { 705 int temp = sortOrder[k]; 706 sortOrder[k] = sortOrder[k+1]; 707 sortOrder[k+1] = temp; 708 float ftemp = dist[k]; 709 dist[k] = dist[k+1]; 710 dist[k+1] = ftemp; 711 } 712 } 713 } 714 715 originIsland = sortOrder[1]; 716 origin = islands[originIsland].mid; 717 718 focus = islands[sortOrder[2]].mid; 719 720 // determine the projection directions based on the origin island triangle 721 idVec3 dir = focus - origin; 722 dir.Normalize(); 723 724 const idVec3 p1 = idDrawVert::GetSkinnedDrawVertPosition( srcTri->verts[srcTri->indexes[islands[originIsland].tris[0] + 0]], joints ); 725 const idVec3 p2 = idDrawVert::GetSkinnedDrawVertPosition( srcTri->verts[srcTri->indexes[islands[originIsland].tris[0] + 1]], joints ); 726 const idVec3 p3 = idDrawVert::GetSkinnedDrawVertPosition( srcTri->verts[srcTri->indexes[islands[originIsland].tris[0] + 2]], joints ); 727 728 idVec3 v1 = p2 - p1; 729 v1.Normalize(); 730 idVec3 v2 = p3 - p1; 731 v2.Normalize(); 732 733 // texVec[0] will be the normal to the origin triangle 734 idVec3 texVec[2]; 735 texVec[0].Cross( v1, v2 ); 736 texVec[1].Cross( texVec[0], dir ); 737 738 for ( int j = 0; j < 2; j++ ) { 739 texVec[j] -= dir * ( texVec[j] * dir ); 740 texVec[j].Normalize(); 741 } 742 743 // emit these triangles, generating the projected texcoords 744 for ( int j = 0; j < islands[i].numTris; j++ ) { 745 for ( int k = 0; k < 3; k++ ) { 746 int index = islands[i].tris[j] * 3; 747 748 index = srcTri->indexes[index + k]; 749 newIndexes[numIndexes++] = index; 750 751 newVerts[index] = idDrawVert::GetSkinnedDrawVert( srcTri->verts[index], joints ); 752 753 const idVec3 local = newVerts[index].xyz - origin; 754 newVerts[index].SetTexCoord( 0.5f + local * texVec[0], 0.5f + local * texVec[1] ); 755 } 756 } 757 } 758 759 newTri->numIndexes = numIndexes; 760 761 return R_FinishDeform( surf, newTri, newVerts, newIndexes ); 762 } 763 764 /* 765 ===================== 766 R_ParticleDeform 767 768 Emit particles from the surface. 769 ===================== 770 */ 771 static drawSurf_t * R_ParticleDeform( drawSurf_t *surf, bool useArea ) { 772 const renderEntity_t * renderEntity = &surf->space->entityDef->parms; 773 const viewDef_t * viewDef = tr.viewDef; 774 const idDeclParticle * particleSystem = (const idDeclParticle *)surf->material->GetDeformDecl(); 775 const srfTriangles_t * srcTri = surf->frontEndGeo; 776 777 if ( r_skipParticles.GetBool() ) { 778 return NULL; 779 } 780 781 // 782 // calculate the area of all the triangles 783 // 784 int numSourceTris = surf->frontEndGeo->numIndexes / 3; 785 float totalArea = 0.0f; 786 float * sourceTriAreas = NULL; 787 788 const idJointMat * joints = ( ( srcTri->staticModelWithJoints != NULL ) && r_useGPUSkinning.GetBool() ) ? srcTri->staticModelWithJoints->jointsInverted : NULL; 789 790 if ( useArea ) { 791 sourceTriAreas = (float *)_alloca( sizeof( *sourceTriAreas ) * numSourceTris ); 792 int triNum = 0; 793 for ( int i = 0; i < srcTri->numIndexes; i += 3, triNum++ ) { 794 float area = idWinding::TriangleArea( idDrawVert::GetSkinnedDrawVertPosition( srcTri->verts[ srcTri->indexes[ i+0 ] ], joints ), 795 idDrawVert::GetSkinnedDrawVertPosition( srcTri->verts[ srcTri->indexes[ i+1 ] ], joints ), 796 idDrawVert::GetSkinnedDrawVertPosition( srcTri->verts[ srcTri->indexes[ i+2 ] ], joints ) ); 797 sourceTriAreas[triNum] = totalArea; 798 totalArea += area; 799 } 800 } 801 802 // 803 // create the particles almost exactly the way idRenderModelPrt does 804 // 805 particleGen_t g; 806 807 g.renderEnt = renderEntity; 808 g.renderView = &viewDef->renderView; 809 g.origin.Zero(); 810 g.axis = mat3_identity; 811 812 int maxStageParticles[MAX_PARTICLE_STAGES] = { 0 }; 813 int maxStageQuads[MAX_PARTICLE_STAGES] = { 0 }; 814 int maxQuads = 0; 815 816 for ( int stageNum = 0; stageNum < particleSystem->stages.Num(); stageNum++ ) { 817 idParticleStage *stage = particleSystem->stages[stageNum]; 818 819 if ( stage->material == NULL ) { 820 continue; 821 } 822 if ( stage->cycleMsec == 0 ) { 823 continue; 824 } 825 if ( stage->hidden ) { // just for gui particle editor use 826 continue; 827 } 828 829 // we interpret stage->totalParticles as "particles per map square area" 830 // so the systems look the same on different size surfaces 831 const int totalParticles = ( useArea ) ? idMath::Ftoi( stage->totalParticles * totalArea * ( 1.0f / 4096.0f ) ) : ( stage->totalParticles ); 832 const int numQuads = totalParticles * stage->NumQuadsPerParticle() * ( ( useArea ) ? 1 : numSourceTris ); 833 834 maxStageParticles[stageNum] = totalParticles; 835 maxStageQuads[stageNum] = numQuads; 836 maxQuads = Max( maxQuads, numQuads ); 837 } 838 839 if ( maxQuads == 0 ) { 840 return NULL; 841 } 842 843 idTempArray<byte> tempVerts( ALIGN( maxQuads * 4 * sizeof( idDrawVert ), 16 ) ); 844 idDrawVert *newVerts = (idDrawVert *) tempVerts.Ptr(); 845 idTempArray<byte> tempIndex( ALIGN( maxQuads * 6 * sizeof( triIndex_t ), 16 ) ); 846 triIndex_t *newIndexes = (triIndex_t *) tempIndex.Ptr(); 847 848 drawSurf_t * drawSurfList = NULL; 849 850 for ( int stageNum = 0; stageNum < particleSystem->stages.Num(); stageNum++ ) { 851 if ( maxStageQuads[stageNum] == 0 ) { 852 continue; 853 } 854 855 idParticleStage *stage = particleSystem->stages[stageNum]; 856 857 int numVerts = 0; 858 for ( int currentTri = 0; currentTri < ( ( useArea ) ? 1 : numSourceTris ); currentTri++ ) { 859 860 idRandom steppingRandom; 861 idRandom steppingRandom2; 862 863 int stageAge = g.renderView->time[renderEntity->timeGroup] + idMath::Ftoi( renderEntity->shaderParms[SHADERPARM_TIMEOFFSET] * 1000.0f - stage->timeOffset * 1000.0f ); 864 int stageCycle = stageAge / stage->cycleMsec; 865 866 // some particles will be in this cycle, some will be in the previous cycle 867 steppingRandom.SetSeed( ( ( stageCycle << 10 ) & idRandom::MAX_RAND ) ^ idMath::Ftoi( renderEntity->shaderParms[SHADERPARM_DIVERSITY] * idRandom::MAX_RAND ) ); 868 steppingRandom2.SetSeed( ( ( ( stageCycle - 1 ) << 10 ) & idRandom::MAX_RAND ) ^ idMath::Ftoi( renderEntity->shaderParms[SHADERPARM_DIVERSITY] * idRandom::MAX_RAND ) ); 869 870 for ( int index = 0; index < maxStageParticles[stageNum]; index++ ) { 871 g.index = index; 872 873 // bump the random 874 steppingRandom.RandomInt(); 875 steppingRandom2.RandomInt(); 876 877 // calculate local age for this index 878 int bunchOffset = idMath::Ftoi( stage->particleLife * 1000 * stage->spawnBunching * index / maxStageParticles[stageNum] ); 879 880 int particleAge = stageAge - bunchOffset; 881 int particleCycle = particleAge / stage->cycleMsec; 882 if ( particleCycle < 0 ) { 883 // before the particleSystem spawned 884 continue; 885 } 886 if ( stage->cycles != 0.0f && particleCycle >= stage->cycles ) { 887 // cycled systems will only run cycle times 888 continue; 889 } 890 891 int inCycleTime = particleAge - particleCycle * stage->cycleMsec; 892 893 if ( renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME] != 0.0f && 894 g.renderView->time[renderEntity->timeGroup] - inCycleTime >= renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME] * 1000.0f ) { 895 // don't fire any more particles 896 continue; 897 } 898 899 // supress particles before or after the age clamp 900 g.frac = (float)inCycleTime / ( stage->particleLife * 1000.0f ); 901 if ( g.frac < 0.0f ) { 902 // yet to be spawned 903 continue; 904 } 905 if ( g.frac > 1.0f ) { 906 // this particle is in the deadTime band 907 continue; 908 } 909 910 if ( particleCycle == stageCycle ) { 911 g.random = steppingRandom; 912 } else { 913 g.random = steppingRandom2; 914 } 915 916 //--------------- 917 // locate the particle origin and axis somewhere on the surface 918 //--------------- 919 920 int pointTri = currentTri; 921 922 if ( useArea ) { 923 // select a triangle based on an even area distribution 924 pointTri = idBinSearch_LessEqual<float>( sourceTriAreas, numSourceTris, g.random.RandomFloat() * totalArea ); 925 } 926 927 // now pick a random point inside pointTri 928 const idDrawVert v1 = idDrawVert::GetSkinnedDrawVert( srcTri->verts[ srcTri->indexes[ pointTri * 3 + 0 ] ], joints ); 929 const idDrawVert v2 = idDrawVert::GetSkinnedDrawVert( srcTri->verts[ srcTri->indexes[ pointTri * 3 + 1 ] ], joints ); 930 const idDrawVert v3 = idDrawVert::GetSkinnedDrawVert( srcTri->verts[ srcTri->indexes[ pointTri * 3 + 2 ] ], joints ); 931 932 float f1 = g.random.RandomFloat(); 933 float f2 = g.random.RandomFloat(); 934 float f3 = g.random.RandomFloat(); 935 936 float ft = 1.0f / ( f1 + f2 + f3 + 0.0001f ); 937 938 f1 *= ft; 939 f2 *= ft; 940 f3 *= ft; 941 942 g.origin = v1.xyz * f1 + v2.xyz * f2 + v3.xyz * f3; 943 g.axis[0] = v1.GetTangent() * f1 + v2.GetTangent() * f2 + v3.GetTangent() * f3; 944 g.axis[1] = v1.GetBiTangent() * f1 + v2.GetBiTangent() * f2 + v3.GetBiTangent() * f3; 945 g.axis[2] = v1.GetNormal() * f1 + v2.GetNormal() * f2 + v3.GetNormal() * f3; 946 947 // this is needed so aimed particles can calculate origins at different times 948 g.originalRandom = g.random; 949 950 g.age = g.frac * stage->particleLife; 951 952 // if the particle doesn't get drawn because it is faded out or beyond a kill region, 953 // don't increment the verts 954 numVerts += stage->CreateParticle( &g, newVerts + numVerts ); 955 } 956 } 957 958 if ( numVerts == 0 ) { 959 continue; 960 } 961 962 // build the index list 963 int numIndexes = 0; 964 for ( int i = 0; i < numVerts; i += 4 ) { 965 newIndexes[numIndexes + 0] = i + 0; 966 newIndexes[numIndexes + 1] = i + 2; 967 newIndexes[numIndexes + 2] = i + 3; 968 newIndexes[numIndexes + 3] = i + 0; 969 newIndexes[numIndexes + 4] = i + 3; 970 newIndexes[numIndexes + 5] = i + 1; 971 numIndexes += 6; 972 } 973 974 // allocate a srfTriangles in temp memory that can hold all the particles 975 srfTriangles_t * newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES ); 976 newTri->bounds = stage->bounds; // just always draw the particles 977 newTri->numVerts = numVerts; 978 newTri->numIndexes = numIndexes; 979 newTri->ambientCache = vertexCache.AllocVertex( newVerts, ALIGN( numVerts * sizeof( idDrawVert ), VERTEX_CACHE_ALIGN ) ); 980 newTri->indexCache = vertexCache.AllocIndex( newIndexes, ALIGN( numIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) ); 981 982 drawSurf_t * drawSurf = (drawSurf_t *)R_FrameAlloc( sizeof( *drawSurf ), FRAME_ALLOC_DRAW_SURFACE ); 983 drawSurf->frontEndGeo = newTri; 984 drawSurf->numIndexes = newTri->numIndexes; 985 drawSurf->ambientCache = newTri->ambientCache; 986 drawSurf->indexCache = newTri->indexCache; 987 drawSurf->shadowCache = 0; 988 drawSurf->jointCache = 0; 989 drawSurf->space = surf->space; 990 drawSurf->scissorRect = surf->scissorRect; 991 drawSurf->extraGLState = 0; 992 drawSurf->renderZFail = 0; 993 994 R_SetupDrawSurfShader( drawSurf, stage->material, renderEntity ); 995 996 drawSurf->linkChain = NULL; 997 drawSurf->nextOnLight = drawSurfList; 998 drawSurfList = drawSurf; 999 } 1000 1001 return drawSurfList; 1002 } 1003 1004 /* 1005 ================= 1006 R_DeformDrawSurf 1007 ================= 1008 */ 1009 drawSurf_t * R_DeformDrawSurf( drawSurf_t * drawSurf ) { 1010 if ( drawSurf->material == NULL ) { 1011 return NULL; 1012 } 1013 1014 if ( r_skipDeforms.GetBool() ) { 1015 return drawSurf; 1016 } 1017 switch ( drawSurf->material->Deform() ) { 1018 case DFRM_SPRITE: return R_AutospriteDeform( drawSurf ); 1019 case DFRM_TUBE: return R_TubeDeform( drawSurf ); 1020 case DFRM_FLARE: return R_FlareDeform( drawSurf ); 1021 case DFRM_EXPAND: return R_ExpandDeform( drawSurf ); 1022 case DFRM_MOVE: return R_MoveDeform( drawSurf ); 1023 case DFRM_TURB: return R_TurbulentDeform( drawSurf ); 1024 case DFRM_EYEBALL: return R_EyeballDeform( drawSurf ); 1025 case DFRM_PARTICLE: return R_ParticleDeform( drawSurf, true ); 1026 case DFRM_PARTICLE2: return R_ParticleDeform( drawSurf, false ); 1027 default: return NULL; 1028 } 1029 }