DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

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 }