1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030 |
- /*
- ===========================================================================
- Doom 3 BFG Edition GPL Source Code
- Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
- Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
- 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.
- 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.
- ===========================================================================
- */
- #pragma hdrstop
- #include "../idlib/precompiled.h"
- #include "tr_local.h"
- #include "Model_local.h"
- /*
- ==========================================================================================
- DEFORM SURFACES
- ==========================================================================================
- */
- /*
- =================
- R_FinishDeform
- =================
- */
- static drawSurf_t * R_FinishDeform( drawSurf_t * surf, srfTriangles_t * newTri, const idDrawVert * newVerts, const triIndex_t * newIndexes ) {
- newTri->ambientCache = vertexCache.AllocVertex( newVerts, ALIGN( newTri->numVerts * sizeof( idDrawVert ), VERTEX_CACHE_ALIGN ) );
- newTri->indexCache = vertexCache.AllocIndex( newIndexes, ALIGN( newTri->numIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) );
- surf->frontEndGeo = newTri;
- surf->numIndexes = newTri->numIndexes;
- surf->ambientCache = newTri->ambientCache;
- surf->indexCache = newTri->indexCache;
- surf->shadowCache = 0;
- surf->jointCache = 0;
- surf->nextOnLight = NULL;
- return surf;
- }
- /*
- =====================
- R_AutospriteDeform
- Assuming all the triangles for this shader are independant
- quads, rebuild them as forward facing sprites.
- =====================
- */
- static drawSurf_t * R_AutospriteDeform( drawSurf_t *surf ) {
- const srfTriangles_t * srcTri = surf->frontEndGeo;
- if ( srcTri->numVerts & 3 ) {
- common->Warning( "R_AutospriteDeform: shader had odd vertex count" );
- return NULL;
- }
- if ( srcTri->numIndexes != ( srcTri->numVerts >> 2 ) * 6 ) {
- common->Warning( "R_AutospriteDeform: autosprite had odd index count" );
- return NULL;
- }
- const idJointMat * joints = ( srcTri->staticModelWithJoints != NULL && r_useGPUSkinning.GetBool() ) ? srcTri->staticModelWithJoints->jointsInverted : NULL;
- idVec3 leftDir;
- idVec3 upDir;
- R_GlobalVectorToLocal( surf->space->modelMatrix, tr.viewDef->renderView.viewaxis[1], leftDir );
- R_GlobalVectorToLocal( surf->space->modelMatrix, tr.viewDef->renderView.viewaxis[2], upDir );
- if ( tr.viewDef->isMirror ) {
- leftDir = vec3_origin - leftDir;
- }
- // the srfTriangles_t are in frame memory and will be automatically disposed of
- srfTriangles_t * newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES );
- newTri->numVerts = srcTri->numVerts;
- newTri->numIndexes = srcTri->numIndexes;
- idDrawVert * newVerts = (idDrawVert *)_alloca16( ALIGN( srcTri->numVerts * sizeof( idDrawVert ), 16 ) );
- triIndex_t * newIndexes = (triIndex_t *)_alloca16( ALIGN( srcTri->numIndexes * sizeof( triIndex_t ), 16 ) );
- for ( int i = 0; i < srcTri->numVerts; i += 4 ) {
- // find the midpoint
- newVerts[i+0] = idDrawVert::GetSkinnedDrawVert( srcTri->verts[i + 0], joints );
- newVerts[i+1] = idDrawVert::GetSkinnedDrawVert( srcTri->verts[i + 1], joints );
- newVerts[i+2] = idDrawVert::GetSkinnedDrawVert( srcTri->verts[i + 2], joints );
- newVerts[i+3] = idDrawVert::GetSkinnedDrawVert( srcTri->verts[i + 3], joints );
- idVec3 mid;
- mid[0] = 0.25f * ( newVerts[i+0].xyz[0] + newVerts[i+1].xyz[0] + newVerts[i+2].xyz[0] + newVerts[i+3].xyz[0] );
- mid[1] = 0.25f * ( newVerts[i+0].xyz[1] + newVerts[i+1].xyz[1] + newVerts[i+2].xyz[1] + newVerts[i+3].xyz[1] );
- mid[2] = 0.25f * ( newVerts[i+0].xyz[2] + newVerts[i+1].xyz[2] + newVerts[i+2].xyz[2] + newVerts[i+3].xyz[2] );
- const idVec3 delta = newVerts[i+0].xyz - mid;
- const float radius = delta.Length() * idMath::SQRT_1OVER2;
- const idVec3 left = leftDir * radius;
- const idVec3 up = upDir * radius;
- newVerts[i+0].xyz = mid + left + up;
- newVerts[i+0].SetTexCoord( 0, 0 );
- newVerts[i+1].xyz = mid - left + up;
- newVerts[i+1].SetTexCoord( 1, 0 );
- newVerts[i+2].xyz = mid - left - up;
- newVerts[i+2].SetTexCoord( 1, 1 );
- newVerts[i+3].xyz = mid + left - up;
- newVerts[i+3].SetTexCoord( 0, 1 );
- newIndexes[6*(i>>2)+0] = i+0;
- newIndexes[6*(i>>2)+1] = i+1;
- newIndexes[6*(i>>2)+2] = i+2;
- newIndexes[6*(i>>2)+3] = i+0;
- newIndexes[6*(i>>2)+4] = i+2;
- newIndexes[6*(i>>2)+5] = i+3;
- }
- return R_FinishDeform( surf, newTri, newVerts, newIndexes );
- }
- /*
- =====================
- R_TubeDeform
- Will pivot a rectangular quad along the center of its long axis.
- Note that a geometric tube with even quite a few sides will almost certainly render
- much faster than this, so this should only be for faked volumetric tubes.
- Make sure this is used with twosided translucent shaders, because the exact side
- order may not be correct.
- =====================
- */
- static drawSurf_t * R_TubeDeform( drawSurf_t * surf ) {
- static int edgeVerts[6][2] = {
- { 0, 1 },
- { 1, 2 },
- { 2, 0 },
- { 3, 4 },
- { 4, 5 },
- { 5, 3 }
- };
- const srfTriangles_t * srcTri = surf->frontEndGeo;
- if ( srcTri->numVerts & 3 ) {
- common->Error( "R_TubeDeform: shader had odd vertex count" );
- }
- if ( srcTri->numIndexes != ( srcTri->numVerts >> 2 ) * 6 ) {
- common->Error( "R_TubeDeform: autosprite had odd index count" );
- }
- const idJointMat * joints = ( srcTri->staticModelWithJoints != NULL && r_useGPUSkinning.GetBool() ) ? srcTri->staticModelWithJoints->jointsInverted : NULL;
- // we need the view direction to project the minor axis of the tube
- // as the view changes
- idVec3 localView;
- R_GlobalPointToLocal( surf->space->modelMatrix, tr.viewDef->renderView.vieworg, localView );
- // the srfTriangles_t are in frame memory and will be automatically disposed of
- srfTriangles_t * newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES );
- newTri->numVerts = srcTri->numVerts;
- newTri->numIndexes = srcTri->numIndexes;
- idDrawVert * newVerts = (idDrawVert *)_alloca16( ALIGN( srcTri->numVerts * sizeof( idDrawVert ), 16 ) );
- for ( int i = 0; i < srcTri->numVerts; i++ ) {
- newVerts[i].Clear();
- }
- // this is a lot of work for two triangles...
- // we could precalculate a lot if it is an issue, but it would mess up the shader abstraction
- for ( int i = 0, indexes = 0; i < srcTri->numVerts; i += 4, indexes += 6 ) {
- // identify the two shortest edges out of the six defined by the indexes
- int nums[2] = { 0, 0 };
- float lengths[2] = { 999999.0f, 999999.0f };
- for ( int j = 0; j < 6; j++ ) {
- const idVec3 v1 = idDrawVert::GetSkinnedDrawVertPosition( srcTri->verts[srcTri->indexes[i + edgeVerts[j][0]]], joints );
- const idVec3 v2 = idDrawVert::GetSkinnedDrawVertPosition( srcTri->verts[srcTri->indexes[i + edgeVerts[j][1]]], joints );
- const float l = ( v1 - v2 ).Length();
- if ( l < lengths[0] ) {
- nums[1] = nums[0];
- lengths[1] = lengths[0];
- nums[0] = j;
- lengths[0] = l;
- } else if ( l < lengths[1] ) {
- nums[1] = j;
- lengths[1] = l;
- }
- }
- // find the midpoints of the two short edges, which
- // will give us the major axis in object coordinates
- idVec3 mid[2];
- for ( int j = 0; j < 2; j++ ) {
- const idVec3 v1 = idDrawVert::GetSkinnedDrawVertPosition( srcTri->verts[srcTri->indexes[i+edgeVerts[nums[j]][0]]], joints );
- const idVec3 v2 = idDrawVert::GetSkinnedDrawVertPosition( srcTri->verts[srcTri->indexes[i+edgeVerts[nums[j]][1]]], joints );
- mid[j][0] = 0.5f * ( v1[0] + v2[0] );
- mid[j][1] = 0.5f * ( v1[1] + v2[1] );
- mid[j][2] = 0.5f * ( v1[2] + v2[2] );
- }
- // find the vector of the major axis
- const idVec3 major = mid[1] - mid[0];
- // re-project the points
- for ( int j = 0; j < 2; j++ ) {
- const int i1 = srcTri->indexes[i+edgeVerts[nums[j]][0]];
- const int i2 = srcTri->indexes[i+edgeVerts[nums[j]][1]];
- newVerts[i1] = idDrawVert::GetSkinnedDrawVert( srcTri->verts[i1], joints );
- newVerts[i2] = idDrawVert::GetSkinnedDrawVert( srcTri->verts[i2], joints );
- const float l = 0.5f * lengths[j];
-
- // cross this with the view direction to get minor axis
- idVec3 dir = mid[j] - localView;
- idVec3 minor;
- minor.Cross( major, dir );
- minor.Normalize();
- if ( j ) {
- newVerts[i1].xyz = mid[j] - l * minor;
- newVerts[i2].xyz = mid[j] + l * minor;
- } else {
- newVerts[i1].xyz = mid[j] + l * minor;
- newVerts[i2].xyz = mid[j] - l * minor;
- }
- }
- }
- return R_FinishDeform( surf, newTri, newVerts, srcTri->indexes );
- }
- /*
- =====================
- R_WindingFromTriangles
- =====================
- */
- #define MAX_TRI_WINDING_INDEXES 16
- int R_WindingFromTriangles( const srfTriangles_t *tri, triIndex_t indexes[MAX_TRI_WINDING_INDEXES] ) {
- int i, j, k, l;
- indexes[0] = tri->indexes[0];
- int numIndexes = 1;
- int numTris = tri->numIndexes / 3;
- do {
- // find an edge that goes from the current index to another
- // index that isn't already used, and isn't an internal edge
- for ( i = 0; i < numTris; i++ ) {
- for ( j = 0; j < 3; j++ ) {
- if ( tri->indexes[i*3+j] != indexes[numIndexes-1] ) {
- continue;
- }
- int next = tri->indexes[i*3+(j+1)%3];
- // make sure it isn't already used
- if ( numIndexes == 1 ) {
- if ( next == indexes[0] ) {
- continue;
- }
- } else {
- for ( k = 1; k < numIndexes; k++ ) {
- if ( indexes[k] == next ) {
- break;
- }
- }
- if ( k != numIndexes ) {
- continue;
- }
- }
- // make sure it isn't an interior edge
- for ( k = 0; k < numTris; k++ ) {
- if ( k == i ) {
- continue;
- }
- for ( l = 0; l < 3; l++ ) {
- int a, b;
- a = tri->indexes[k*3+l];
- if ( a != next ) {
- continue;
- }
- b = tri->indexes[k*3+(l+1)%3];
- if ( b != indexes[numIndexes-1] ) {
- continue;
- }
- // this is an interior edge
- break;
- }
- if ( l != 3 ) {
- break;
- }
- }
- if ( k != numTris ) {
- continue;
- }
- // add this to the list
- indexes[numIndexes] = next;
- numIndexes++;
- break;
- }
- if ( j != 3 ) {
- break;
- }
- }
- if ( numIndexes == tri->numVerts ) {
- break;
- }
- } while ( i != numTris );
- return numIndexes;
- }
- /*
- =====================
- R_FlareDeform
- =====================
- */
- static drawSurf_t * R_FlareDeform( drawSurf_t * surf ) {
- const srfTriangles_t * srcTri = surf->frontEndGeo;
- assert( srcTri->staticModelWithJoints == NULL );
- if ( srcTri->numVerts != 4 || srcTri->numIndexes != 6 ) {
- // FIXME: temp hack for flares on tripleted models
- common->Warning( "R_FlareDeform: not a single quad" );
- return NULL;
- }
- // find the plane
- idPlane plane;
- plane.FromPoints( srcTri->verts[srcTri->indexes[0]].xyz, srcTri->verts[srcTri->indexes[1]].xyz, srcTri->verts[srcTri->indexes[2]].xyz );
- // if viewer is behind the plane, draw nothing
- idVec3 localViewer;
- R_GlobalPointToLocal( surf->space->modelMatrix, tr.viewDef->renderView.vieworg, localViewer );
- float distFromPlane = localViewer * plane.Normal() + plane[3];
- if ( distFromPlane <= 0 ) {
- return NULL;
- }
- idVec3 center = srcTri->verts[0].xyz;
- for ( int i = 1; i < srcTri->numVerts; i++ ) {
- center += srcTri->verts[i].xyz;
- }
- center *= 1.0f / srcTri->numVerts;
- idVec3 dir = localViewer - center;
- dir.Normalize();
- const float dot = dir * plane.Normal();
- // set vertex colors based on plane angle
- int color = idMath::Ftoi( dot * 8 * 256 );
- if ( color > 255 ) {
- color = 255;
- }
- triIndex_t indexes[MAX_TRI_WINDING_INDEXES];
- int numIndexes = R_WindingFromTriangles( srcTri, indexes );
- // only deal with quads
- if ( numIndexes != 4 ) {
- return NULL;
- }
- const int maxVerts = 16;
- const int maxIndexes = 18 * 3;
- // the srfTriangles_t are in frame memory and will be automatically disposed of
- srfTriangles_t * newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES );
- newTri->numVerts = maxVerts;
- newTri->numIndexes = maxIndexes;
- idDrawVert *newVerts = (idDrawVert *)_alloca16( ALIGN( maxVerts * sizeof( idDrawVert ), 16 ) );
- idVec3 edgeDir[4][3];
- // calculate vector directions
- for ( int i = 0; i < 4; i++ ) {
- newVerts[i].Clear();
- newVerts[i].xyz = srcTri->verts[ indexes[i] ].xyz;
- newVerts[i].SetTexCoord( 0.5f, 0.5f );
- newVerts[i].color[0] = color;
- newVerts[i].color[1] = color;
- newVerts[i].color[2] = color;
- newVerts[i].color[3] = 255;
- idVec3 toEye = srcTri->verts[ indexes[i] ].xyz - localViewer;
- toEye.Normalize();
- idVec3 d1 = srcTri->verts[ indexes[(i+1)%4] ].xyz - localViewer;
- d1.Normalize();
- edgeDir[i][2].Cross( toEye, d1 );
- edgeDir[i][2].Normalize();
- edgeDir[i][2] = vec3_origin - edgeDir[i][2];
- idVec3 d2 = srcTri->verts[ indexes[(i+3)%4] ].xyz - localViewer;
- d2.Normalize();
- edgeDir[i][0].Cross( toEye, d2 );
- edgeDir[i][0].Normalize();
- edgeDir[i][1] = edgeDir[i][0] + edgeDir[i][2];
- edgeDir[i][1].Normalize();
- }
- const float spread = surf->shaderRegisters[ surf->material->GetDeformRegister(0) ] * r_flareSize.GetFloat();
- for ( int i = 4; i < 16; i++ ) {
- const int index = ( i - 4 ) / 3;
- idVec3 v = srcTri->verts[indexes[index]].xyz + spread * edgeDir[index][( i - 4 ) % 3];
- idVec3 dir = v - localViewer;
- const float len = dir.Normalize();
- const float ang = dir * plane.Normal();
- const float newLen = -( distFromPlane / ang );
- if ( newLen > 0.0f && newLen < len ) {
- v = localViewer + dir * newLen;
- }
- newVerts[i].Clear();
- newVerts[i].xyz = v;
- newVerts[i].SetTexCoord( 0.0f, 0.5f );
- newVerts[i].color[0] = color;
- newVerts[i].color[1] = color;
- newVerts[i].color[2] = color;
- newVerts[i].color[3] = 255;
- }
- ALIGNTYPE16 static triIndex_t triIndexes[18*3 + 10] = {
- 0, 4,5, 0,5, 6, 0,6,7, 0,7, 1, 1,7, 8, 1,8, 9,
- 15, 4,0, 15,0, 3, 3,0,1, 3,1, 2, 2,1, 9, 2,9,10,
- 14,15,3, 14,3,13, 13,3,2, 13,2,12, 12,2,11, 11,2,10,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // to make this a multiple of 16 bytes
- };
- return R_FinishDeform( surf, newTri, newVerts, triIndexes );
- }
- /*
- =====================
- R_ExpandDeform
- Expands the surface along it's normals by a shader amount
- =====================
- */
- static drawSurf_t * R_ExpandDeform( drawSurf_t * surf ) {
- const srfTriangles_t * srcTri = surf->frontEndGeo;
- assert( srcTri->staticModelWithJoints == NULL );
- // the srfTriangles_t are in frame memory and will be automatically disposed of
- srfTriangles_t * newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES );
- newTri->numVerts = srcTri->numVerts;
- newTri->numIndexes = srcTri->numIndexes;
- idDrawVert *newVerts = (idDrawVert *)_alloca16( ALIGN( newTri->numVerts * sizeof( idDrawVert ), 16 ) );
- const float dist = surf->shaderRegisters[ surf->material->GetDeformRegister(0) ];
- for ( int i = 0; i < srcTri->numVerts; i++ ) {
- newVerts[i] = srcTri->verts[i];
- newVerts[i].xyz = srcTri->verts[i].xyz + srcTri->verts[i].GetNormal() * dist;
- }
- return R_FinishDeform( surf, newTri, newVerts, srcTri->indexes );
- }
- /*
- =====================
- R_MoveDeform
- Moves the surface along the X axis, mostly just for demoing the deforms
- =====================
- */
- static drawSurf_t * R_MoveDeform( drawSurf_t * surf ) {
- const srfTriangles_t * srcTri = surf->frontEndGeo;
- assert( srcTri->staticModelWithJoints == NULL );
- // the srfTriangles_t are in frame memory and will be automatically disposed of
- srfTriangles_t * newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES );
- newTri->numVerts = srcTri->numVerts;
- newTri->numIndexes = srcTri->numIndexes;
- idDrawVert *newVerts = (idDrawVert *)_alloca16( ALIGN( newTri->numVerts * sizeof( idDrawVert ), 16 ) );
- const float dist = surf->shaderRegisters[ surf->material->GetDeformRegister(0) ];
- for ( int i = 0; i < srcTri->numVerts; i++ ) {
- newVerts[i] = srcTri->verts[i];
- newVerts[i].xyz[0] += dist;
- }
- return R_FinishDeform( surf, newTri, newVerts, srcTri->indexes );
- }
- /*
- =====================
- R_TurbulentDeform
- Turbulently deforms the texture coordinates.
- =====================
- */
- static drawSurf_t * R_TurbulentDeform( drawSurf_t * surf ) {
- const srfTriangles_t * srcTri = surf->frontEndGeo;
- assert( srcTri->staticModelWithJoints == NULL );
- // the srfTriangles_t are in frame memory and will be automatically disposed of
- srfTriangles_t * newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES );
- newTri->numVerts = srcTri->numVerts;
- newTri->numIndexes = srcTri->numIndexes;
- idDrawVert *newVerts = (idDrawVert *)_alloca16( ALIGN( newTri->numVerts * sizeof( idDrawVert ), 16 ) );
- const idDeclTable * table = (const idDeclTable *)surf->material->GetDeformDecl();
- const float range = surf->shaderRegisters[ surf->material->GetDeformRegister(0) ];
- const float timeOfs = surf->shaderRegisters[ surf->material->GetDeformRegister(1) ];
- const float domain = surf->shaderRegisters[ surf->material->GetDeformRegister(2) ];
- const float tOfs = 0.5f;
- for ( int i = 0; i < srcTri->numVerts; i++ ) {
- float f = srcTri->verts[i].xyz[0] * 0.003f + srcTri->verts[i].xyz[1] * 0.007f + srcTri->verts[i].xyz[2] * 0.011f;
- f = timeOfs + domain * f;
- f += timeOfs;
- idVec2 tempST = srcTri->verts[i].GetTexCoord();
- tempST[0] += range * table->TableLookup( f );
- tempST[1] += range * table->TableLookup( f + tOfs );
- newVerts[i] = srcTri->verts[i];
- newVerts[i].SetTexCoord( tempST );
- }
- return R_FinishDeform( surf, newTri, newVerts, srcTri->indexes );
- }
- /*
- =====================
- AddTriangleToIsland_r
- =====================
- */
- #define MAX_EYEBALL_TRIS 10
- #define MAX_EYEBALL_ISLANDS 6
- typedef struct {
- int tris[MAX_EYEBALL_TRIS];
- int numTris;
- idBounds bounds;
- idVec3 mid;
- } eyeIsland_t;
- static void AddTriangleToIsland_r( const srfTriangles_t *tri, int triangleNum, bool *usedList, eyeIsland_t *island ) {
- usedList[triangleNum] = true;
- // add to the current island
- if ( island->numTris >= MAX_EYEBALL_TRIS ) {
- common->Error( "MAX_EYEBALL_TRIS" );
- return;
- }
- island->tris[island->numTris] = triangleNum;
- island->numTris++;
- const idJointMat * joints = ( tri->staticModelWithJoints != NULL && r_useGPUSkinning.GetBool() ) ? tri->staticModelWithJoints->jointsInverted : NULL;
- // recurse into all neighbors
- const int a = tri->indexes[triangleNum*3+0];
- const int b = tri->indexes[triangleNum*3+1];
- const int c = tri->indexes[triangleNum*3+2];
- const idVec3 va = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[a], joints );
- const idVec3 vb = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[b], joints );
- const idVec3 vc = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[c], joints );
- island->bounds.AddPoint( va );
- island->bounds.AddPoint( vb );
- island->bounds.AddPoint( vc );
- int numTri = tri->numIndexes / 3;
- for ( int i = 0; i < numTri; i++ ) {
- if ( usedList[i] ) {
- continue;
- }
- if ( tri->indexes[i*3+0] == a
- || tri->indexes[i*3+1] == a
- || tri->indexes[i*3+2] == a
- || tri->indexes[i*3+0] == b
- || tri->indexes[i*3+1] == b
- || tri->indexes[i*3+2] == b
- || tri->indexes[i*3+0] == c
- || tri->indexes[i*3+1] == c
- || tri->indexes[i*3+2] == c ) {
- AddTriangleToIsland_r( tri, i, usedList, island );
- }
- }
- }
- /*
- =====================
- R_EyeballDeform
- Each eyeball surface should have an separate upright triangle behind it, long end
- pointing out the eye, and another single triangle in front of the eye for the focus point.
- =====================
- */
- static drawSurf_t * R_EyeballDeform( drawSurf_t * surf ) {
- const srfTriangles_t * srcTri = surf->frontEndGeo;
- // separate all the triangles into islands
- const int numTri = srcTri->numIndexes / 3;
- if ( numTri > MAX_EYEBALL_ISLANDS * MAX_EYEBALL_TRIS ) {
- common->Printf( "R_EyeballDeform: too many triangles in surface" );
- return NULL;
- }
- eyeIsland_t islands[MAX_EYEBALL_ISLANDS];
- bool triUsed[MAX_EYEBALL_ISLANDS*MAX_EYEBALL_TRIS];
- memset( triUsed, 0, sizeof( triUsed ) );
- int numIslands = 0;
- for ( ; numIslands < MAX_EYEBALL_ISLANDS; numIslands++ ) {
- islands[numIslands].numTris = 0;
- islands[numIslands].bounds.Clear();
- int i;
- for ( i = 0; i < numTri; i++ ) {
- if ( !triUsed[i] ) {
- AddTriangleToIsland_r( srcTri, i, triUsed, &islands[numIslands] );
- break;
- }
- }
- if ( i == numTri ) {
- break;
- }
- }
- // assume we always have two eyes, two origins, and two targets
- if ( numIslands != 3 ) {
- common->Printf( "R_EyeballDeform: %i triangle islands\n", numIslands );
- return NULL;
- }
- const idJointMat * joints = ( srcTri->staticModelWithJoints != NULL && r_useGPUSkinning.GetBool() ) ? srcTri->staticModelWithJoints->jointsInverted : NULL;
- // the srfTriangles_t are in frame memory and will be automatically disposed of
- srfTriangles_t * newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES );
- newTri->numVerts = srcTri->numVerts;
- newTri->numIndexes = srcTri->numIndexes;
- idDrawVert *newVerts = (idDrawVert *)_alloca16( ALIGN( srcTri->numVerts * sizeof( idDrawVert ), 16 ) );
- triIndex_t *newIndexes = (triIndex_t *)_alloca16( ALIGN( srcTri->numIndexes * sizeof( triIndex_t ), 16 ) );
- // decide which islands are the eyes and points
- for ( int i = 0; i < numIslands; i++ ) {
- islands[i].mid = islands[i].bounds.GetCenter();
- }
- int numIndexes = 0;
- for ( int i = 0; i < numIslands; i++ ) {
- eyeIsland_t * island = &islands[i];
- if ( island->numTris == 1 ) {
- continue;
- }
- // the closest single triangle point will be the eye origin
- // and the next-to-farthest will be the focal point
- idVec3 origin;
- idVec3 focus;
- int originIsland = 0;
- float dist[MAX_EYEBALL_ISLANDS];
- int sortOrder[MAX_EYEBALL_ISLANDS];
- for ( int j = 0; j < numIslands; j++ ) {
- idVec3 dir = islands[j].mid - island->mid;
- dist[j] = dir.Length();
- sortOrder[j] = j;
- for ( int k = j - 1; k >= 0; k-- ) {
- if ( dist[k] > dist[k+1] ) {
- int temp = sortOrder[k];
- sortOrder[k] = sortOrder[k+1];
- sortOrder[k+1] = temp;
- float ftemp = dist[k];
- dist[k] = dist[k+1];
- dist[k+1] = ftemp;
- }
- }
- }
- originIsland = sortOrder[1];
- origin = islands[originIsland].mid;
- focus = islands[sortOrder[2]].mid;
- // determine the projection directions based on the origin island triangle
- idVec3 dir = focus - origin;
- dir.Normalize();
- const idVec3 p1 = idDrawVert::GetSkinnedDrawVertPosition( srcTri->verts[srcTri->indexes[islands[originIsland].tris[0] + 0]], joints );
- const idVec3 p2 = idDrawVert::GetSkinnedDrawVertPosition( srcTri->verts[srcTri->indexes[islands[originIsland].tris[0] + 1]], joints );
- const idVec3 p3 = idDrawVert::GetSkinnedDrawVertPosition( srcTri->verts[srcTri->indexes[islands[originIsland].tris[0] + 2]], joints );
- idVec3 v1 = p2 - p1;
- v1.Normalize();
- idVec3 v2 = p3 - p1;
- v2.Normalize();
- // texVec[0] will be the normal to the origin triangle
- idVec3 texVec[2];
- texVec[0].Cross( v1, v2 );
- texVec[1].Cross( texVec[0], dir );
- for ( int j = 0; j < 2; j++ ) {
- texVec[j] -= dir * ( texVec[j] * dir );
- texVec[j].Normalize();
- }
- // emit these triangles, generating the projected texcoords
- for ( int j = 0; j < islands[i].numTris; j++ ) {
- for ( int k = 0; k < 3; k++ ) {
- int index = islands[i].tris[j] * 3;
- index = srcTri->indexes[index + k];
- newIndexes[numIndexes++] = index;
- newVerts[index] = idDrawVert::GetSkinnedDrawVert( srcTri->verts[index], joints );
- const idVec3 local = newVerts[index].xyz - origin;
- newVerts[index].SetTexCoord( 0.5f + local * texVec[0], 0.5f + local * texVec[1] );
- }
- }
- }
- newTri->numIndexes = numIndexes;
- return R_FinishDeform( surf, newTri, newVerts, newIndexes );
- }
- /*
- =====================
- R_ParticleDeform
- Emit particles from the surface.
- =====================
- */
- static drawSurf_t * R_ParticleDeform( drawSurf_t *surf, bool useArea ) {
- const renderEntity_t * renderEntity = &surf->space->entityDef->parms;
- const viewDef_t * viewDef = tr.viewDef;
- const idDeclParticle * particleSystem = (const idDeclParticle *)surf->material->GetDeformDecl();
- const srfTriangles_t * srcTri = surf->frontEndGeo;
- if ( r_skipParticles.GetBool() ) {
- return NULL;
- }
- //
- // calculate the area of all the triangles
- //
- int numSourceTris = surf->frontEndGeo->numIndexes / 3;
- float totalArea = 0.0f;
- float * sourceTriAreas = NULL;
- const idJointMat * joints = ( ( srcTri->staticModelWithJoints != NULL ) && r_useGPUSkinning.GetBool() ) ? srcTri->staticModelWithJoints->jointsInverted : NULL;
- if ( useArea ) {
- sourceTriAreas = (float *)_alloca( sizeof( *sourceTriAreas ) * numSourceTris );
- int triNum = 0;
- for ( int i = 0; i < srcTri->numIndexes; i += 3, triNum++ ) {
- float area = idWinding::TriangleArea( idDrawVert::GetSkinnedDrawVertPosition( srcTri->verts[ srcTri->indexes[ i+0 ] ], joints ),
- idDrawVert::GetSkinnedDrawVertPosition( srcTri->verts[ srcTri->indexes[ i+1 ] ], joints ),
- idDrawVert::GetSkinnedDrawVertPosition( srcTri->verts[ srcTri->indexes[ i+2 ] ], joints ) );
- sourceTriAreas[triNum] = totalArea;
- totalArea += area;
- }
- }
- //
- // create the particles almost exactly the way idRenderModelPrt does
- //
- particleGen_t g;
- g.renderEnt = renderEntity;
- g.renderView = &viewDef->renderView;
- g.origin.Zero();
- g.axis = mat3_identity;
- int maxStageParticles[MAX_PARTICLE_STAGES] = { 0 };
- int maxStageQuads[MAX_PARTICLE_STAGES] = { 0 };
- int maxQuads = 0;
- for ( int stageNum = 0; stageNum < particleSystem->stages.Num(); stageNum++ ) {
- idParticleStage *stage = particleSystem->stages[stageNum];
- if ( stage->material == NULL ) {
- continue;
- }
- if ( stage->cycleMsec == 0 ) {
- continue;
- }
- if ( stage->hidden ) { // just for gui particle editor use
- continue;
- }
- // we interpret stage->totalParticles as "particles per map square area"
- // so the systems look the same on different size surfaces
- const int totalParticles = ( useArea ) ? idMath::Ftoi( stage->totalParticles * totalArea * ( 1.0f / 4096.0f ) ) : ( stage->totalParticles );
- const int numQuads = totalParticles * stage->NumQuadsPerParticle() * ( ( useArea ) ? 1 : numSourceTris );
- maxStageParticles[stageNum] = totalParticles;
- maxStageQuads[stageNum] = numQuads;
- maxQuads = Max( maxQuads, numQuads );
- }
- if ( maxQuads == 0 ) {
- return NULL;
- }
- idTempArray<byte> tempVerts( ALIGN( maxQuads * 4 * sizeof( idDrawVert ), 16 ) );
- idDrawVert *newVerts = (idDrawVert *) tempVerts.Ptr();
- idTempArray<byte> tempIndex( ALIGN( maxQuads * 6 * sizeof( triIndex_t ), 16 ) );
- triIndex_t *newIndexes = (triIndex_t *) tempIndex.Ptr();
- drawSurf_t * drawSurfList = NULL;
- for ( int stageNum = 0; stageNum < particleSystem->stages.Num(); stageNum++ ) {
- if ( maxStageQuads[stageNum] == 0 ) {
- continue;
- }
- idParticleStage *stage = particleSystem->stages[stageNum];
- int numVerts = 0;
- for ( int currentTri = 0; currentTri < ( ( useArea ) ? 1 : numSourceTris ); currentTri++ ) {
- idRandom steppingRandom;
- idRandom steppingRandom2;
- int stageAge = g.renderView->time[renderEntity->timeGroup] + idMath::Ftoi( renderEntity->shaderParms[SHADERPARM_TIMEOFFSET] * 1000.0f - stage->timeOffset * 1000.0f );
- int stageCycle = stageAge / stage->cycleMsec;
- // some particles will be in this cycle, some will be in the previous cycle
- steppingRandom.SetSeed( ( ( stageCycle << 10 ) & idRandom::MAX_RAND ) ^ idMath::Ftoi( renderEntity->shaderParms[SHADERPARM_DIVERSITY] * idRandom::MAX_RAND ) );
- steppingRandom2.SetSeed( ( ( ( stageCycle - 1 ) << 10 ) & idRandom::MAX_RAND ) ^ idMath::Ftoi( renderEntity->shaderParms[SHADERPARM_DIVERSITY] * idRandom::MAX_RAND ) );
- for ( int index = 0; index < maxStageParticles[stageNum]; index++ ) {
- g.index = index;
- // bump the random
- steppingRandom.RandomInt();
- steppingRandom2.RandomInt();
- // calculate local age for this index
- int bunchOffset = idMath::Ftoi( stage->particleLife * 1000 * stage->spawnBunching * index / maxStageParticles[stageNum] );
- int particleAge = stageAge - bunchOffset;
- int particleCycle = particleAge / stage->cycleMsec;
- if ( particleCycle < 0 ) {
- // before the particleSystem spawned
- continue;
- }
- if ( stage->cycles != 0.0f && particleCycle >= stage->cycles ) {
- // cycled systems will only run cycle times
- continue;
- }
- int inCycleTime = particleAge - particleCycle * stage->cycleMsec;
- if ( renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME] != 0.0f &&
- g.renderView->time[renderEntity->timeGroup] - inCycleTime >= renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME] * 1000.0f ) {
- // don't fire any more particles
- continue;
- }
- // supress particles before or after the age clamp
- g.frac = (float)inCycleTime / ( stage->particleLife * 1000.0f );
- if ( g.frac < 0.0f ) {
- // yet to be spawned
- continue;
- }
- if ( g.frac > 1.0f ) {
- // this particle is in the deadTime band
- continue;
- }
- if ( particleCycle == stageCycle ) {
- g.random = steppingRandom;
- } else {
- g.random = steppingRandom2;
- }
- //---------------
- // locate the particle origin and axis somewhere on the surface
- //---------------
- int pointTri = currentTri;
- if ( useArea ) {
- // select a triangle based on an even area distribution
- pointTri = idBinSearch_LessEqual<float>( sourceTriAreas, numSourceTris, g.random.RandomFloat() * totalArea );
- }
- // now pick a random point inside pointTri
- const idDrawVert v1 = idDrawVert::GetSkinnedDrawVert( srcTri->verts[ srcTri->indexes[ pointTri * 3 + 0 ] ], joints );
- const idDrawVert v2 = idDrawVert::GetSkinnedDrawVert( srcTri->verts[ srcTri->indexes[ pointTri * 3 + 1 ] ], joints );
- const idDrawVert v3 = idDrawVert::GetSkinnedDrawVert( srcTri->verts[ srcTri->indexes[ pointTri * 3 + 2 ] ], joints );
- float f1 = g.random.RandomFloat();
- float f2 = g.random.RandomFloat();
- float f3 = g.random.RandomFloat();
- float ft = 1.0f / ( f1 + f2 + f3 + 0.0001f );
- f1 *= ft;
- f2 *= ft;
- f3 *= ft;
- g.origin = v1.xyz * f1 + v2.xyz * f2 + v3.xyz * f3;
- g.axis[0] = v1.GetTangent() * f1 + v2.GetTangent() * f2 + v3.GetTangent() * f3;
- g.axis[1] = v1.GetBiTangent() * f1 + v2.GetBiTangent() * f2 + v3.GetBiTangent() * f3;
- g.axis[2] = v1.GetNormal() * f1 + v2.GetNormal() * f2 + v3.GetNormal() * f3;
- // this is needed so aimed particles can calculate origins at different times
- g.originalRandom = g.random;
- g.age = g.frac * stage->particleLife;
- // if the particle doesn't get drawn because it is faded out or beyond a kill region,
- // don't increment the verts
- numVerts += stage->CreateParticle( &g, newVerts + numVerts );
- }
- }
-
- if ( numVerts == 0 ) {
- continue;
- }
- // build the index list
- int numIndexes = 0;
- for ( int i = 0; i < numVerts; i += 4 ) {
- newIndexes[numIndexes + 0] = i + 0;
- newIndexes[numIndexes + 1] = i + 2;
- newIndexes[numIndexes + 2] = i + 3;
- newIndexes[numIndexes + 3] = i + 0;
- newIndexes[numIndexes + 4] = i + 3;
- newIndexes[numIndexes + 5] = i + 1;
- numIndexes += 6;
- }
- // allocate a srfTriangles in temp memory that can hold all the particles
- srfTriangles_t * newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES );
- newTri->bounds = stage->bounds; // just always draw the particles
- newTri->numVerts = numVerts;
- newTri->numIndexes = numIndexes;
- newTri->ambientCache = vertexCache.AllocVertex( newVerts, ALIGN( numVerts * sizeof( idDrawVert ), VERTEX_CACHE_ALIGN ) );
- newTri->indexCache = vertexCache.AllocIndex( newIndexes, ALIGN( numIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) );
- drawSurf_t * drawSurf = (drawSurf_t *)R_FrameAlloc( sizeof( *drawSurf ), FRAME_ALLOC_DRAW_SURFACE );
- drawSurf->frontEndGeo = newTri;
- drawSurf->numIndexes = newTri->numIndexes;
- drawSurf->ambientCache = newTri->ambientCache;
- drawSurf->indexCache = newTri->indexCache;
- drawSurf->shadowCache = 0;
- drawSurf->jointCache = 0;
- drawSurf->space = surf->space;
- drawSurf->scissorRect = surf->scissorRect;
- drawSurf->extraGLState = 0;
- drawSurf->renderZFail = 0;
- R_SetupDrawSurfShader( drawSurf, stage->material, renderEntity );
- drawSurf->linkChain = NULL;
- drawSurf->nextOnLight = drawSurfList;
- drawSurfList = drawSurf;
- }
- return drawSurfList;
- }
- /*
- =================
- R_DeformDrawSurf
- =================
- */
- drawSurf_t * R_DeformDrawSurf( drawSurf_t * drawSurf ) {
- if ( drawSurf->material == NULL ) {
- return NULL;
- }
- if ( r_skipDeforms.GetBool() ) {
- return drawSurf;
- }
- switch ( drawSurf->material->Deform() ) {
- case DFRM_SPRITE: return R_AutospriteDeform( drawSurf );
- case DFRM_TUBE: return R_TubeDeform( drawSurf );
- case DFRM_FLARE: return R_FlareDeform( drawSurf );
- case DFRM_EXPAND: return R_ExpandDeform( drawSurf );
- case DFRM_MOVE: return R_MoveDeform( drawSurf );
- case DFRM_TURB: return R_TurbulentDeform( drawSurf );
- case DFRM_EYEBALL: return R_EyeballDeform( drawSurf );
- case DFRM_PARTICLE: return R_ParticleDeform( drawSurf, true );
- case DFRM_PARTICLE2: return R_ParticleDeform( drawSurf, false );
- default: return NULL;
- }
- }
|