123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620 |
- /*
- ===========================================================================
- Doom 3 GPL Source Code
- Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
- Doom 3 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 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 Source Code. If not, see <http://www.gnu.org/licenses/>.
- In addition, the Doom 3 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 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.
- ===========================================================================
- */
- #include "../idlib/precompiled.h"
- #pragma hdrstop
- #include "tr_local.h"
- static const float CHECK_BOUNDS_EPSILON = 1.0f;
- /*
- ===========================================================================================
- VERTEX CACHE GENERATORS
- ===========================================================================================
- */
- /*
- ==================
- R_CreateAmbientCache
- Create it if needed
- ==================
- */
- bool R_CreateAmbientCache( srfTriangles_t *tri, bool needsLighting ) {
- if ( tri->ambientCache ) {
- return true;
- }
- // we are going to use it for drawing, so make sure we have the tangents and normals
- if ( needsLighting && !tri->tangentsCalculated ) {
- R_DeriveTangents( tri );
- }
- vertexCache.Alloc( tri->verts, tri->numVerts * sizeof( tri->verts[0] ), &tri->ambientCache );
- if ( !tri->ambientCache ) {
- return false;
- }
- return true;
- }
- /*
- ==================
- R_CreateLightingCache
- Returns false if the cache couldn't be allocated, in which case the surface should be skipped.
- ==================
- */
- bool R_CreateLightingCache( const idRenderEntityLocal *ent, const idRenderLightLocal *light, srfTriangles_t *tri ) {
- idVec3 localLightOrigin;
- // fogs and blends don't need light vectors
- if ( light->lightShader->IsFogLight() || light->lightShader->IsBlendLight() ) {
- return true;
- }
- // not needed if we have vertex programs
- if ( tr.backEndRendererHasVertexPrograms ) {
- return true;
- }
- R_GlobalPointToLocal( ent->modelMatrix, light->globalLightOrigin, localLightOrigin );
- int size = tri->ambientSurface->numVerts * sizeof( lightingCache_t );
- lightingCache_t *cache = (lightingCache_t *)_alloca16( size );
- #if 1
- SIMDProcessor->CreateTextureSpaceLightVectors( &cache[0].localLightVector, localLightOrigin,
- tri->ambientSurface->verts, tri->ambientSurface->numVerts, tri->indexes, tri->numIndexes );
- #else
- bool *used = (bool *)_alloca16( tri->ambientSurface->numVerts * sizeof( used[0] ) );
- memset( used, 0, tri->ambientSurface->numVerts * sizeof( used[0] ) );
- // because the interaction may be a very small subset of the full surface,
- // it makes sense to only deal with the verts used
- for ( int j = 0; j < tri->numIndexes; j++ ) {
- int i = tri->indexes[j];
- if ( used[i] ) {
- continue;
- }
- used[i] = true;
- idVec3 lightDir;
- const idDrawVert *v;
- v = &tri->ambientSurface->verts[i];
- lightDir = localLightOrigin - v->xyz;
- cache[i].localLightVector[0] = lightDir * v->tangents[0];
- cache[i].localLightVector[1] = lightDir * v->tangents[1];
- cache[i].localLightVector[2] = lightDir * v->normal;
- }
- #endif
- vertexCache.Alloc( cache, size, &tri->lightingCache );
- if ( !tri->lightingCache ) {
- return false;
- }
- return true;
- }
- /*
- ==================
- R_CreatePrivateShadowCache
- This is used only for a specific light
- ==================
- */
- void R_CreatePrivateShadowCache( srfTriangles_t *tri ) {
- if ( !tri->shadowVertexes ) {
- return;
- }
- vertexCache.Alloc( tri->shadowVertexes, tri->numVerts * sizeof( *tri->shadowVertexes ), &tri->shadowCache );
- }
- /*
- ==================
- R_CreateVertexProgramShadowCache
- This is constant for any number of lights, the vertex program
- takes care of projecting the verts to infinity.
- ==================
- */
- void R_CreateVertexProgramShadowCache( srfTriangles_t *tri ) {
- if ( tri->verts == NULL ) {
- return;
- }
- shadowCache_t *temp = (shadowCache_t *)_alloca16( tri->numVerts * 2 * sizeof( shadowCache_t ) );
- #if 1
- SIMDProcessor->CreateVertexProgramShadowCache( &temp->xyz, tri->verts, tri->numVerts );
- #else
- int numVerts = tri->numVerts;
- const idDrawVert *verts = tri->verts;
- for ( int i = 0; i < numVerts; i++ ) {
- const float *v = verts[i].xyz.ToFloatPtr();
- temp[i*2+0].xyz[0] = v[0];
- temp[i*2+1].xyz[0] = v[0];
- temp[i*2+0].xyz[1] = v[1];
- temp[i*2+1].xyz[1] = v[1];
- temp[i*2+0].xyz[2] = v[2];
- temp[i*2+1].xyz[2] = v[2];
- temp[i*2+0].xyz[3] = 1.0f; // on the model surface
- temp[i*2+1].xyz[3] = 0.0f; // will be projected to infinity
- }
- #endif
- vertexCache.Alloc( temp, tri->numVerts * 2 * sizeof( shadowCache_t ), &tri->shadowCache );
- }
- /*
- ==================
- R_SkyboxTexGen
- ==================
- */
- void R_SkyboxTexGen( drawSurf_t *surf, const idVec3 &viewOrg ) {
- int i;
- idVec3 localViewOrigin;
- R_GlobalPointToLocal( surf->space->modelMatrix, viewOrg, localViewOrigin );
- int numVerts = surf->geo->numVerts;
- int size = numVerts * sizeof( idVec3 );
- idVec3 *texCoords = (idVec3 *) _alloca16( size );
- const idDrawVert *verts = surf->geo->verts;
- for ( i = 0; i < numVerts; i++ ) {
- texCoords[i][0] = verts[i].xyz[0] - localViewOrigin[0];
- texCoords[i][1] = verts[i].xyz[1] - localViewOrigin[1];
- texCoords[i][2] = verts[i].xyz[2] - localViewOrigin[2];
- }
- surf->dynamicTexCoords = vertexCache.AllocFrameTemp( texCoords, size );
- }
- /*
- ==================
- R_WobbleskyTexGen
- ==================
- */
- void R_WobbleskyTexGen( drawSurf_t *surf, const idVec3 &viewOrg ) {
- int i;
- idVec3 localViewOrigin;
- const int *parms = surf->material->GetTexGenRegisters();
- float wobbleDegrees = surf->shaderRegisters[ parms[0] ];
- float wobbleSpeed = surf->shaderRegisters[ parms[1] ];
- float rotateSpeed = surf->shaderRegisters[ parms[2] ];
- wobbleDegrees = wobbleDegrees * idMath::PI / 180;
- wobbleSpeed = wobbleSpeed * 2 * idMath::PI / 60;
- rotateSpeed = rotateSpeed * 2 * idMath::PI / 60;
- // very ad-hoc "wobble" transform
- float transform[16];
- float a = tr.viewDef->floatTime * wobbleSpeed;
- float s = sin( a ) * sin( wobbleDegrees );
- float c = cos( a ) * sin( wobbleDegrees );
- float z = cos( wobbleDegrees );
- idVec3 axis[3];
- axis[2][0] = c;
- axis[2][1] = s;
- axis[2][2] = z;
- axis[1][0] = -sin( a * 2 ) * sin( wobbleDegrees );
- axis[1][2] = -s * sin( wobbleDegrees );
- axis[1][1] = sqrt( 1.0f - ( axis[1][0] * axis[1][0] + axis[1][2] * axis[1][2] ) );
- // make the second vector exactly perpendicular to the first
- axis[1] -= ( axis[2] * axis[1] ) * axis[2];
- axis[1].Normalize();
- // construct the third with a cross
- axis[0].Cross( axis[1], axis[2] );
- // add the rotate
- s = sin( rotateSpeed * tr.viewDef->floatTime );
- c = cos( rotateSpeed * tr.viewDef->floatTime );
- transform[0] = axis[0][0] * c + axis[1][0] * s;
- transform[4] = axis[0][1] * c + axis[1][1] * s;
- transform[8] = axis[0][2] * c + axis[1][2] * s;
- transform[1] = axis[1][0] * c - axis[0][0] * s;
- transform[5] = axis[1][1] * c - axis[0][1] * s;
- transform[9] = axis[1][2] * c - axis[0][2] * s;
- transform[2] = axis[2][0];
- transform[6] = axis[2][1];
- transform[10] = axis[2][2];
- transform[3] = transform[7] = transform[11] = 0.0f;
- transform[12] = transform[13] = transform[14] = 0.0f;
- R_GlobalPointToLocal( surf->space->modelMatrix, viewOrg, localViewOrigin );
- int numVerts = surf->geo->numVerts;
- int size = numVerts * sizeof( idVec3 );
- idVec3 *texCoords = (idVec3 *) _alloca16( size );
- const idDrawVert *verts = surf->geo->verts;
- for ( i = 0; i < numVerts; i++ ) {
- idVec3 v;
- v[0] = verts[i].xyz[0] - localViewOrigin[0];
- v[1] = verts[i].xyz[1] - localViewOrigin[1];
- v[2] = verts[i].xyz[2] - localViewOrigin[2];
- R_LocalPointToGlobal( transform, v, texCoords[i] );
- }
- surf->dynamicTexCoords = vertexCache.AllocFrameTemp( texCoords, size );
- }
- /*
- =================
- R_SpecularTexGen
- Calculates the specular coordinates for cards without vertex programs.
- =================
- */
- static void R_SpecularTexGen( drawSurf_t *surf, const idVec3 &globalLightOrigin, const idVec3 &viewOrg ) {
- const srfTriangles_t *tri;
- idVec3 localLightOrigin;
- idVec3 localViewOrigin;
- R_GlobalPointToLocal( surf->space->modelMatrix, globalLightOrigin, localLightOrigin );
- R_GlobalPointToLocal( surf->space->modelMatrix, viewOrg, localViewOrigin );
- tri = surf->geo;
- // FIXME: change to 3 component?
- int size = tri->numVerts * sizeof( idVec4 );
- idVec4 *texCoords = (idVec4 *) _alloca16( size );
- #if 1
- SIMDProcessor->CreateSpecularTextureCoords( texCoords, localLightOrigin, localViewOrigin,
- tri->verts, tri->numVerts, tri->indexes, tri->numIndexes );
- #else
- bool *used = (bool *)_alloca16( tri->numVerts * sizeof( used[0] ) );
- memset( used, 0, tri->numVerts * sizeof( used[0] ) );
- // because the interaction may be a very small subset of the full surface,
- // it makes sense to only deal with the verts used
- for ( int j = 0; j < tri->numIndexes; j++ ) {
- int i = tri->indexes[j];
- if ( used[i] ) {
- continue;
- }
- used[i] = true;
- float ilength;
- const idDrawVert *v = &tri->verts[i];
- idVec3 lightDir = localLightOrigin - v->xyz;
- idVec3 viewDir = localViewOrigin - v->xyz;
- ilength = idMath::RSqrt( lightDir * lightDir );
- lightDir[0] *= ilength;
- lightDir[1] *= ilength;
- lightDir[2] *= ilength;
- ilength = idMath::RSqrt( viewDir * viewDir );
- viewDir[0] *= ilength;
- viewDir[1] *= ilength;
- viewDir[2] *= ilength;
- lightDir += viewDir;
- texCoords[i][0] = lightDir * v->tangents[0];
- texCoords[i][1] = lightDir * v->tangents[1];
- texCoords[i][2] = lightDir * v->normal;
- texCoords[i][3] = 1;
- }
- #endif
- surf->dynamicTexCoords = vertexCache.AllocFrameTemp( texCoords, size );
- }
- //=======================================================================================================
- /*
- =============
- R_SetEntityDefViewEntity
- If the entityDef isn't already on the viewEntity list, create
- a viewEntity and add it to the list with an empty scissor rect.
- This does not instantiate dynamic models for the entity yet.
- =============
- */
- viewEntity_t *R_SetEntityDefViewEntity( idRenderEntityLocal *def ) {
- viewEntity_t *vModel;
- if ( def->viewCount == tr.viewCount ) {
- return def->viewEntity;
- }
- def->viewCount = tr.viewCount;
- // set the model and modelview matricies
- vModel = (viewEntity_t *)R_ClearedFrameAlloc( sizeof( *vModel ) );
- vModel->entityDef = def;
- // the scissorRect will be expanded as the model bounds is accepted into visible portal chains
- vModel->scissorRect.Clear();
- // copy the model and weapon depth hack for back-end use
- vModel->modelDepthHack = def->parms.modelDepthHack;
- vModel->weaponDepthHack = def->parms.weaponDepthHack;
- R_AxisToModelMatrix( def->parms.axis, def->parms.origin, vModel->modelMatrix );
- // we may not have a viewDef if we are just creating shadows at entity creation time
- if ( tr.viewDef ) {
- myGlMultMatrix( vModel->modelMatrix, tr.viewDef->worldSpace.modelViewMatrix, vModel->modelViewMatrix );
- vModel->next = tr.viewDef->viewEntitys;
- tr.viewDef->viewEntitys = vModel;
- }
- def->viewEntity = vModel;
- return vModel;
- }
- /*
- ====================
- R_TestPointInViewLight
- ====================
- */
- static const float INSIDE_LIGHT_FRUSTUM_SLOP = 32;
- // this needs to be greater than the dist from origin to corner of near clip plane
- static bool R_TestPointInViewLight( const idVec3 &org, const idRenderLightLocal *light ) {
- int i;
- idVec3 local;
- for ( i = 0 ; i < 6 ; i++ ) {
- float d = light->frustum[i].Distance( org );
- if ( d > INSIDE_LIGHT_FRUSTUM_SLOP ) {
- return false;
- }
- }
- return true;
- }
- /*
- ===================
- R_PointInFrustum
- Assumes positive sides face outward
- ===================
- */
- static bool R_PointInFrustum( idVec3 &p, idPlane *planes, int numPlanes ) {
- for ( int i = 0 ; i < numPlanes ; i++ ) {
- float d = planes[i].Distance( p );
- if ( d > 0 ) {
- return false;
- }
- }
- return true;
- }
- /*
- =============
- R_SetLightDefViewLight
- If the lightDef isn't already on the viewLight list, create
- a viewLight and add it to the list with an empty scissor rect.
- =============
- */
- viewLight_t *R_SetLightDefViewLight( idRenderLightLocal *light ) {
- viewLight_t *vLight;
- if ( light->viewCount == tr.viewCount ) {
- return light->viewLight;
- }
- light->viewCount = tr.viewCount;
- // add to the view light chain
- vLight = (viewLight_t *)R_ClearedFrameAlloc( sizeof( *vLight ) );
- vLight->lightDef = light;
- // the scissorRect will be expanded as the light bounds is accepted into visible portal chains
- vLight->scissorRect.Clear();
- // calculate the shadow cap optimization states
- vLight->viewInsideLight = R_TestPointInViewLight( tr.viewDef->renderView.vieworg, light );
- if ( !vLight->viewInsideLight ) {
- vLight->viewSeesShadowPlaneBits = 0;
- for ( int i = 0 ; i < light->numShadowFrustums ; i++ ) {
- float d = light->shadowFrustums[i].planes[5].Distance( tr.viewDef->renderView.vieworg );
- if ( d < INSIDE_LIGHT_FRUSTUM_SLOP ) {
- vLight->viewSeesShadowPlaneBits|= 1 << i;
- }
- }
- } else {
- // this should not be referenced in this case
- vLight->viewSeesShadowPlaneBits = 63;
- }
- // see if the light center is in view, which will allow us to cull invisible shadows
- vLight->viewSeesGlobalLightOrigin = R_PointInFrustum( light->globalLightOrigin, tr.viewDef->frustum, 4 );
- // copy data used by backend
- vLight->globalLightOrigin = light->globalLightOrigin;
- vLight->lightProject[0] = light->lightProject[0];
- vLight->lightProject[1] = light->lightProject[1];
- vLight->lightProject[2] = light->lightProject[2];
- vLight->lightProject[3] = light->lightProject[3];
- vLight->fogPlane = light->frustum[5];
- vLight->frustumTris = light->frustumTris;
- vLight->falloffImage = light->falloffImage;
- vLight->lightShader = light->lightShader;
- vLight->shaderRegisters = NULL; // allocated and evaluated in R_AddLightSurfaces
- // link the view light
- vLight->next = tr.viewDef->viewLights;
- tr.viewDef->viewLights = vLight;
- light->viewLight = vLight;
- return vLight;
- }
- /*
- =================
- idRenderWorldLocal::CreateLightDefInteractions
- When a lightDef is determined to effect the view (contact the frustum and non-0 light), it will check to
- make sure that it has interactions for all the entityDefs that it might possibly contact.
- This does not guarantee that all possible interactions for this light are generated, only that
- the ones that may effect the current view are generated. so it does need to be called every view.
- This does not cause entityDefs to create dynamic models, all work is done on the referenceBounds.
- All entities that have non-empty interactions with viewLights will
- have viewEntities made for them and be put on the viewEntity list,
- even if their surfaces aren't visible, because they may need to cast shadows.
- Interactions are usually removed when a entityDef or lightDef is modified, unless the change
- is known to not effect them, so there is no danger of getting a stale interaction, we just need to
- check that needed ones are created.
- An interaction can be at several levels:
- Don't interact (but share an area) (numSurfaces = 0)
- Entity reference bounds touches light frustum, but surfaces haven't been generated (numSurfaces = -1)
- Shadow surfaces have been generated, but light surfaces have not. The shadow surface may still be empty due to bounds being conservative.
- Both shadow and light surfaces have been generated. Either or both surfaces may still be empty due to conservative bounds.
- =================
- */
- void idRenderWorldLocal::CreateLightDefInteractions( idRenderLightLocal *ldef ) {
- areaReference_t *eref;
- areaReference_t *lref;
- idRenderEntityLocal *edef;
- portalArea_t *area;
- idInteraction *inter;
- for ( lref = ldef->references ; lref ; lref = lref->ownerNext ) {
- area = lref->area;
- // check all the models in this area
- for ( eref = area->entityRefs.areaNext ; eref != &area->entityRefs ; eref = eref->areaNext ) {
- edef = eref->entity;
- // if the entity doesn't have any light-interacting surfaces, we could skip this,
- // but we don't want to instantiate dynamic models yet, so we can't check that on
- // most things
- // if the entity isn't viewed
- if ( tr.viewDef && edef->viewCount != tr.viewCount ) {
- // if the light doesn't cast shadows, skip
- if ( !ldef->lightShader->LightCastsShadows() ) {
- continue;
- }
- // if we are suppressing its shadow in this view, skip
- if ( !r_skipSuppress.GetBool() ) {
- if ( edef->parms.suppressShadowInViewID && edef->parms.suppressShadowInViewID == tr.viewDef->renderView.viewID ) {
- continue;
- }
- if ( edef->parms.suppressShadowInLightID && edef->parms.suppressShadowInLightID == ldef->parms.lightId ) {
- continue;
- }
- }
- }
- // some big outdoor meshes are flagged to not create any dynamic interactions
- // when the level designer knows that nearby moving lights shouldn't actually hit them
- if ( edef->parms.noDynamicInteractions && edef->world->generateAllInteractionsCalled ) {
- continue;
- }
- // if any of the edef's interaction match this light, we don't
- // need to consider it.
- if ( r_useInteractionTable.GetBool() && this->interactionTable ) {
- // allocating these tables may take several megs on big maps, but it saves 3% to 5% of
- // the CPU time. The table is updated at interaction::AllocAndLink() and interaction::UnlinkAndFree()
- int index = ldef->index * this->interactionTableWidth + edef->index;
- inter = this->interactionTable[ index ];
- if ( inter ) {
- // if this entity wasn't in view already, the scissor rect will be empty,
- // so it will only be used for shadow casting
- if ( !inter->IsEmpty() ) {
- R_SetEntityDefViewEntity( edef );
- }
- continue;
- }
- } else {
- // scan the doubly linked lists, which may have several dozen entries
- // we could check either model refs or light refs for matches, but it is
- // assumed that there will be less lights in an area than models
- // so the entity chains should be somewhat shorter (they tend to be fairly close).
- for ( inter = edef->firstInteraction; inter != NULL; inter = inter->entityNext ) {
- if ( inter->lightDef == ldef ) {
- break;
- }
- }
- // if we already have an interaction, we don't need to do anything
- if ( inter != NULL ) {
- // if this entity wasn't in view already, the scissor rect will be empty,
- // so it will only be used for shadow casting
- if ( !inter->IsEmpty() ) {
- R_SetEntityDefViewEntity( edef );
- }
- continue;
- }
- }
- //
- // create a new interaction, but don't do any work other than bbox to frustum culling
- //
- idInteraction *inter = idInteraction::AllocAndLink( edef, ldef );
- // do a check of the entity reference bounds against the light frustum,
- // trying to avoid creating a viewEntity if it hasn't been already
- float modelMatrix[16];
- float *m;
- if ( edef->viewCount == tr.viewCount ) {
- m = edef->viewEntity->modelMatrix;
- } else {
- R_AxisToModelMatrix( edef->parms.axis, edef->parms.origin, modelMatrix );
- m = modelMatrix;
- }
- if ( R_CullLocalBox( edef->referenceBounds, m, 6, ldef->frustum ) ) {
- inter->MakeEmpty();
- continue;
- }
- // we will do a more precise per-surface check when we are checking the entity
- // if this entity wasn't in view already, the scissor rect will be empty,
- // so it will only be used for shadow casting
- R_SetEntityDefViewEntity( edef );
- }
- }
- }
- //===============================================================================================================
- /*
- =================
- R_LinkLightSurf
- =================
- */
- void R_LinkLightSurf( const drawSurf_t **link, const srfTriangles_t *tri, const viewEntity_t *space,
- const idRenderLightLocal *light, const idMaterial *shader, const idScreenRect &scissor, bool viewInsideShadow ) {
- drawSurf_t *drawSurf;
- if ( !space ) {
- space = &tr.viewDef->worldSpace;
- }
- drawSurf = (drawSurf_t *)R_FrameAlloc( sizeof( *drawSurf ) );
- drawSurf->geo = tri;
- drawSurf->space = space;
- drawSurf->material = shader;
- drawSurf->scissorRect = scissor;
- drawSurf->dsFlags = 0;
- if ( viewInsideShadow ) {
- drawSurf->dsFlags |= DSF_VIEW_INSIDE_SHADOW;
- }
- if ( !shader ) {
- // shadows won't have a shader
- drawSurf->shaderRegisters = NULL;
- } else {
- // process the shader expressions for conditionals / color / texcoords
- const float *constRegs = shader->ConstantRegisters();
- if ( constRegs ) {
- // this shader has only constants for parameters
- drawSurf->shaderRegisters = constRegs;
- } else {
- // FIXME: share with the ambient surface?
- float *regs = (float *)R_FrameAlloc( shader->GetNumRegisters() * sizeof( float ) );
- drawSurf->shaderRegisters = regs;
- shader->EvaluateRegisters( regs, space->entityDef->parms.shaderParms, tr.viewDef, space->entityDef->parms.referenceSound );
- }
- // calculate the specular coordinates if we aren't using vertex programs
- if ( !tr.backEndRendererHasVertexPrograms && !r_skipSpecular.GetBool() && tr.backEndRenderer != BE_ARB ) {
- R_SpecularTexGen( drawSurf, light->globalLightOrigin, tr.viewDef->renderView.vieworg );
- // if we failed to allocate space for the specular calculations, drop the surface
- if ( !drawSurf->dynamicTexCoords ) {
- return;
- }
- }
- }
- // actually link it in
- drawSurf->nextOnLight = *link;
- *link = drawSurf;
- }
- /*
- ======================
- R_ClippedLightScissorRectangle
- ======================
- */
- idScreenRect R_ClippedLightScissorRectangle( viewLight_t *vLight ) {
- int i, j;
- const idRenderLightLocal *light = vLight->lightDef;
- idScreenRect r;
- idFixedWinding w;
- r.Clear();
- for ( i = 0 ; i < 6 ; i++ ) {
- const idWinding *ow = light->frustumWindings[i];
- // projected lights may have one of the frustums degenerated
- if ( !ow ) {
- continue;
- }
- // the light frustum planes face out from the light,
- // so the planes that have the view origin on the negative
- // side will be the "back" faces of the light, which must have
- // some fragment inside the portalStack to be visible
- if ( light->frustum[i].Distance( tr.viewDef->renderView.vieworg ) >= 0 ) {
- continue;
- }
- w = *ow;
- // now check the winding against each of the frustum planes
- for ( j = 0; j < 5; j++ ) {
- if ( !w.ClipInPlace( -tr.viewDef->frustum[j] ) ) {
- break;
- }
- }
- // project these points to the screen and add to bounds
- for ( j = 0; j < w.GetNumPoints(); j++ ) {
- idPlane eye, clip;
- idVec3 ndc;
- R_TransformModelToClip( w[j].ToVec3(), tr.viewDef->worldSpace.modelViewMatrix, tr.viewDef->projectionMatrix, eye, clip );
- if ( clip[3] <= 0.01f ) {
- clip[3] = 0.01f;
- }
- R_TransformClipToDevice( clip, tr.viewDef, ndc );
- float windowX = 0.5f * ( 1.0f + ndc[0] ) * ( tr.viewDef->viewport.x2 - tr.viewDef->viewport.x1 );
- float windowY = 0.5f * ( 1.0f + ndc[1] ) * ( tr.viewDef->viewport.y2 - tr.viewDef->viewport.y1 );
- if ( windowX > tr.viewDef->scissor.x2 ) {
- windowX = tr.viewDef->scissor.x2;
- } else if ( windowX < tr.viewDef->scissor.x1 ) {
- windowX = tr.viewDef->scissor.x1;
- }
- if ( windowY > tr.viewDef->scissor.y2 ) {
- windowY = tr.viewDef->scissor.y2;
- } else if ( windowY < tr.viewDef->scissor.y1 ) {
- windowY = tr.viewDef->scissor.y1;
- }
- r.AddPoint( windowX, windowY );
- }
- }
- // add the fudge boundary
- r.Expand();
- return r;
- }
- /*
- ==================
- R_CalcLightScissorRectangle
- The light screen bounds will be used to crop the scissor rect during
- stencil clears and interaction drawing
- ==================
- */
- int c_clippedLight, c_unclippedLight;
- idScreenRect R_CalcLightScissorRectangle( viewLight_t *vLight ) {
- idScreenRect r;
- srfTriangles_t *tri;
- idPlane eye, clip;
- idVec3 ndc;
- if ( vLight->lightDef->parms.pointLight ) {
- idBounds bounds;
- idRenderLightLocal *lightDef = vLight->lightDef;
- tr.viewDef->viewFrustum.ProjectionBounds( idBox( lightDef->parms.origin, lightDef->parms.lightRadius, lightDef->parms.axis ), bounds );
- return R_ScreenRectFromViewFrustumBounds( bounds );
- }
- if ( r_useClippedLightScissors.GetInteger() == 2 ) {
- return R_ClippedLightScissorRectangle( vLight );
- }
- r.Clear();
- tri = vLight->lightDef->frustumTris;
- for ( int i = 0 ; i < tri->numVerts ; i++ ) {
- R_TransformModelToClip( tri->verts[i].xyz, tr.viewDef->worldSpace.modelViewMatrix,
- tr.viewDef->projectionMatrix, eye, clip );
- // if it is near clipped, clip the winding polygons to the view frustum
- if ( clip[3] <= 1 ) {
- c_clippedLight++;
- if ( r_useClippedLightScissors.GetInteger() ) {
- return R_ClippedLightScissorRectangle( vLight );
- } else {
- r.x1 = r.y1 = 0;
- r.x2 = ( tr.viewDef->viewport.x2 - tr.viewDef->viewport.x1 ) - 1;
- r.y2 = ( tr.viewDef->viewport.y2 - tr.viewDef->viewport.y1 ) - 1;
- return r;
- }
- }
- R_TransformClipToDevice( clip, tr.viewDef, ndc );
- float windowX = 0.5f * ( 1.0f + ndc[0] ) * ( tr.viewDef->viewport.x2 - tr.viewDef->viewport.x1 );
- float windowY = 0.5f * ( 1.0f + ndc[1] ) * ( tr.viewDef->viewport.y2 - tr.viewDef->viewport.y1 );
- if ( windowX > tr.viewDef->scissor.x2 ) {
- windowX = tr.viewDef->scissor.x2;
- } else if ( windowX < tr.viewDef->scissor.x1 ) {
- windowX = tr.viewDef->scissor.x1;
- }
- if ( windowY > tr.viewDef->scissor.y2 ) {
- windowY = tr.viewDef->scissor.y2;
- } else if ( windowY < tr.viewDef->scissor.y1 ) {
- windowY = tr.viewDef->scissor.y1;
- }
- r.AddPoint( windowX, windowY );
- }
- // add the fudge boundary
- r.Expand();
- c_unclippedLight++;
- return r;
- }
- /*
- =================
- R_AddLightSurfaces
- Calc the light shader values, removing any light from the viewLight list
- if it is determined to not have any visible effect due to being flashed off or turned off.
- Adds entities to the viewEntity list if they are needed for shadow casting.
- Add any precomputed shadow volumes.
- Removes lights from the viewLights list if they are completely
- turned off, or completely off screen.
- Create any new interactions needed between the viewLights
- and the viewEntitys due to game movement
- =================
- */
- void R_AddLightSurfaces( void ) {
- viewLight_t *vLight;
- idRenderLightLocal *light;
- viewLight_t **ptr;
- // go through each visible light, possibly removing some from the list
- ptr = &tr.viewDef->viewLights;
- while ( *ptr ) {
- vLight = *ptr;
- light = vLight->lightDef;
- const idMaterial *lightShader = light->lightShader;
- if ( !lightShader ) {
- common->Error( "R_AddLightSurfaces: NULL lightShader" );
- }
- // see if we are suppressing the light in this view
- if ( !r_skipSuppress.GetBool() ) {
- if ( light->parms.suppressLightInViewID
- && light->parms.suppressLightInViewID == tr.viewDef->renderView.viewID ) {
- *ptr = vLight->next;
- light->viewCount = -1;
- continue;
- }
- if ( light->parms.allowLightInViewID
- && light->parms.allowLightInViewID != tr.viewDef->renderView.viewID ) {
- *ptr = vLight->next;
- light->viewCount = -1;
- continue;
- }
- }
- // evaluate the light shader registers
- float *lightRegs =(float *)R_FrameAlloc( lightShader->GetNumRegisters() * sizeof( float ) );
- vLight->shaderRegisters = lightRegs;
- lightShader->EvaluateRegisters( lightRegs, light->parms.shaderParms, tr.viewDef, light->parms.referenceSound );
- // if this is a purely additive light and no stage in the light shader evaluates
- // to a positive light value, we can completely skip the light
- if ( !lightShader->IsFogLight() && !lightShader->IsBlendLight() ) {
- int lightStageNum;
- for ( lightStageNum = 0 ; lightStageNum < lightShader->GetNumStages() ; lightStageNum++ ) {
- const shaderStage_t *lightStage = lightShader->GetStage( lightStageNum );
- // ignore stages that fail the condition
- if ( !lightRegs[ lightStage->conditionRegister ] ) {
- continue;
- }
- const int *registers = lightStage->color.registers;
- // snap tiny values to zero to avoid lights showing up with the wrong color
- if ( lightRegs[ registers[0] ] < 0.001f ) {
- lightRegs[ registers[0] ] = 0.0f;
- }
- if ( lightRegs[ registers[1] ] < 0.001f ) {
- lightRegs[ registers[1] ] = 0.0f;
- }
- if ( lightRegs[ registers[2] ] < 0.001f ) {
- lightRegs[ registers[2] ] = 0.0f;
- }
- // FIXME: when using the following values the light shows up bright red when using nvidia drivers/hardware
- // this seems to have been fixed ?
- //lightRegs[ registers[0] ] = 1.5143074e-005f;
- //lightRegs[ registers[1] ] = 1.5483369e-005f;
- //lightRegs[ registers[2] ] = 1.7014690e-005f;
- if ( lightRegs[ registers[0] ] > 0.0f ||
- lightRegs[ registers[1] ] > 0.0f ||
- lightRegs[ registers[2] ] > 0.0f ) {
- break;
- }
- }
- if ( lightStageNum == lightShader->GetNumStages() ) {
- // we went through all the stages and didn't find one that adds anything
- // remove the light from the viewLights list, and change its frame marker
- // so interaction generation doesn't think the light is visible and
- // create a shadow for it
- *ptr = vLight->next;
- light->viewCount = -1;
- continue;
- }
- }
- if ( r_useLightScissors.GetBool() ) {
- // calculate the screen area covered by the light frustum
- // which will be used to crop the stencil cull
- idScreenRect scissorRect = R_CalcLightScissorRectangle( vLight );
- // intersect with the portal crossing scissor rectangle
- vLight->scissorRect.Intersect( scissorRect );
- if ( r_showLightScissors.GetBool() ) {
- R_ShowColoredScreenRect( vLight->scissorRect, light->index );
- }
- }
- #if 0
- // this never happens, because CullLightByPortals() does a more precise job
- if ( vLight->scissorRect.IsEmpty() ) {
- // this light doesn't touch anything on screen, so remove it from the list
- *ptr = vLight->next;
- continue;
- }
- #endif
- // this one stays on the list
- ptr = &vLight->next;
- // if we are doing a soft-shadow novelty test, regenerate the light with
- // a random offset every time
- if ( r_lightSourceRadius.GetFloat() != 0.0f ) {
- for ( int i = 0 ; i < 3 ; i++ ) {
- light->globalLightOrigin[i] += r_lightSourceRadius.GetFloat() * ( -1 + 2 * (rand()&0xfff)/(float)0xfff );
- }
- }
- // create interactions with all entities the light may touch, and add viewEntities
- // that may cast shadows, even if they aren't directly visible. Any real work
- // will be deferred until we walk through the viewEntities
- tr.viewDef->renderWorld->CreateLightDefInteractions( light );
- tr.pc.c_viewLights++;
- // fog lights will need to draw the light frustum triangles, so make sure they
- // are in the vertex cache
- if ( lightShader->IsFogLight() ) {
- if ( !light->frustumTris->ambientCache ) {
- if ( !R_CreateAmbientCache( light->frustumTris, false ) ) {
- // skip if we are out of vertex memory
- continue;
- }
- }
- // touch the surface so it won't get purged
- vertexCache.Touch( light->frustumTris->ambientCache );
- }
- // add the prelight shadows for the static world geometry
- if ( light->parms.prelightModel && r_useOptimizedShadows.GetBool() ) {
- if ( !light->parms.prelightModel->NumSurfaces() ) {
- common->Error( "no surfs in prelight model '%s'", light->parms.prelightModel->Name() );
- }
- srfTriangles_t *tri = light->parms.prelightModel->Surface( 0 )->geometry;
- if ( !tri->shadowVertexes ) {
- common->Error( "R_AddLightSurfaces: prelight model '%s' without shadowVertexes", light->parms.prelightModel->Name() );
- }
- // these shadows will all have valid bounds, and can be culled normally
- if ( r_useShadowCulling.GetBool() ) {
- if ( R_CullLocalBox( tri->bounds, tr.viewDef->worldSpace.modelMatrix, 5, tr.viewDef->frustum ) ) {
- continue;
- }
- }
- // if we have been purged, re-upload the shadowVertexes
- if ( !tri->shadowCache ) {
- R_CreatePrivateShadowCache( tri );
- if ( !tri->shadowCache ) {
- continue;
- }
- }
- // touch the shadow surface so it won't get purged
- vertexCache.Touch( tri->shadowCache );
- if ( !tri->indexCache && r_useIndexBuffers.GetBool() ) {
- vertexCache.Alloc( tri->indexes, tri->numIndexes * sizeof( tri->indexes[0] ), &tri->indexCache, true );
- }
- if ( tri->indexCache ) {
- vertexCache.Touch( tri->indexCache );
- }
- R_LinkLightSurf( &vLight->globalShadows, tri, NULL, light, NULL, vLight->scissorRect, true /* FIXME? */ );
- }
- }
- }
- //===============================================================================================================
- /*
- ==================
- R_IssueEntityDefCallback
- ==================
- */
- bool R_IssueEntityDefCallback( idRenderEntityLocal *def ) {
- bool update;
- idBounds oldBounds;
- if ( r_checkBounds.GetBool() ) {
- oldBounds = def->referenceBounds;
- }
- def->archived = false; // will need to be written to the demo file
- tr.pc.c_entityDefCallbacks++;
- if ( tr.viewDef ) {
- update = def->parms.callback( &def->parms, &tr.viewDef->renderView );
- } else {
- update = def->parms.callback( &def->parms, NULL );
- }
- if ( !def->parms.hModel ) {
- common->Error( "R_IssueEntityDefCallback: dynamic entity callback didn't set model" );
- }
- if ( r_checkBounds.GetBool() ) {
- if ( oldBounds[0][0] > def->referenceBounds[0][0] + CHECK_BOUNDS_EPSILON ||
- oldBounds[0][1] > def->referenceBounds[0][1] + CHECK_BOUNDS_EPSILON ||
- oldBounds[0][2] > def->referenceBounds[0][2] + CHECK_BOUNDS_EPSILON ||
- oldBounds[1][0] < def->referenceBounds[1][0] - CHECK_BOUNDS_EPSILON ||
- oldBounds[1][1] < def->referenceBounds[1][1] - CHECK_BOUNDS_EPSILON ||
- oldBounds[1][2] < def->referenceBounds[1][2] - CHECK_BOUNDS_EPSILON ) {
- common->Printf( "entity %i callback extended reference bounds\n", def->index );
- }
- }
- return update;
- }
- /*
- ===================
- R_EntityDefDynamicModel
- Issues a deferred entity callback if necessary.
- If the model isn't dynamic, it returns the original.
- Returns the cached dynamic model if present, otherwise creates
- it and any necessary overlays
- ===================
- */
- idRenderModel *R_EntityDefDynamicModel( idRenderEntityLocal *def ) {
- bool callbackUpdate;
- // allow deferred entities to construct themselves
- if ( def->parms.callback ) {
- callbackUpdate = R_IssueEntityDefCallback( def );
- } else {
- callbackUpdate = false;
- }
- idRenderModel *model = def->parms.hModel;
- if ( !model ) {
- common->Error( "R_EntityDefDynamicModel: NULL model" );
- }
- if ( model->IsDynamicModel() == DM_STATIC ) {
- def->dynamicModel = NULL;
- def->dynamicModelFrameCount = 0;
- return model;
- }
- // continously animating models (particle systems, etc) will have their snapshot updated every single view
- if ( callbackUpdate || ( model->IsDynamicModel() == DM_CONTINUOUS && def->dynamicModelFrameCount != tr.frameCount ) ) {
- R_ClearEntityDefDynamicModel( def );
- }
- // if we don't have a snapshot of the dynamic model, generate it now
- if ( !def->dynamicModel ) {
- // instantiate the snapshot of the dynamic model, possibly reusing memory from the cached snapshot
- def->cachedDynamicModel = model->InstantiateDynamicModel( &def->parms, tr.viewDef, def->cachedDynamicModel );
- if ( def->cachedDynamicModel ) {
- // add any overlays to the snapshot of the dynamic model
- if ( def->overlay && !r_skipOverlays.GetBool() ) {
- def->overlay->AddOverlaySurfacesToModel( def->cachedDynamicModel );
- } else {
- idRenderModelOverlay::RemoveOverlaySurfacesFromModel( def->cachedDynamicModel );
- }
- if ( r_checkBounds.GetBool() ) {
- idBounds b = def->cachedDynamicModel->Bounds();
- if ( b[0][0] < def->referenceBounds[0][0] - CHECK_BOUNDS_EPSILON ||
- b[0][1] < def->referenceBounds[0][1] - CHECK_BOUNDS_EPSILON ||
- b[0][2] < def->referenceBounds[0][2] - CHECK_BOUNDS_EPSILON ||
- b[1][0] > def->referenceBounds[1][0] + CHECK_BOUNDS_EPSILON ||
- b[1][1] > def->referenceBounds[1][1] + CHECK_BOUNDS_EPSILON ||
- b[1][2] > def->referenceBounds[1][2] + CHECK_BOUNDS_EPSILON ) {
- common->Printf( "entity %i dynamic model exceeded reference bounds\n", def->index );
- }
- }
- }
- def->dynamicModel = def->cachedDynamicModel;
- def->dynamicModelFrameCount = tr.frameCount;
- }
- // set model depth hack value
- if ( def->dynamicModel && model->DepthHack() != 0.0f && tr.viewDef ) {
- idPlane eye, clip;
- idVec3 ndc;
- R_TransformModelToClip( def->parms.origin, tr.viewDef->worldSpace.modelViewMatrix, tr.viewDef->projectionMatrix, eye, clip );
- R_TransformClipToDevice( clip, tr.viewDef, ndc );
- def->parms.modelDepthHack = model->DepthHack() * ( 1.0f - ndc.z );
- }
- // FIXME: if any of the surfaces have deforms, create a frame-temporary model with references to the
- // undeformed surfaces. This would allow deforms to be light interacting.
- return def->dynamicModel;
- }
- /*
- =================
- R_AddDrawSurf
- =================
- */
- void R_AddDrawSurf( const srfTriangles_t *tri, const viewEntity_t *space, const renderEntity_t *renderEntity,
- const idMaterial *shader, const idScreenRect &scissor ) {
- drawSurf_t *drawSurf;
- const float *shaderParms;
- static float refRegs[MAX_EXPRESSION_REGISTERS]; // don't put on stack, or VC++ will do a page touch
- float generatedShaderParms[MAX_ENTITY_SHADER_PARMS];
- drawSurf = (drawSurf_t *)R_FrameAlloc( sizeof( *drawSurf ) );
- drawSurf->geo = tri;
- drawSurf->space = space;
- drawSurf->material = shader;
- drawSurf->scissorRect = scissor;
- drawSurf->sort = shader->GetSort() + tr.sortOffset;
- drawSurf->dsFlags = 0;
- // bumping this offset each time causes surfaces with equal sort orders to still
- // deterministically draw in the order they are added
- tr.sortOffset += 0.000001f;
- // if it doesn't fit, resize the list
- if ( tr.viewDef->numDrawSurfs == tr.viewDef->maxDrawSurfs ) {
- drawSurf_t **old = tr.viewDef->drawSurfs;
- int count;
- if ( tr.viewDef->maxDrawSurfs == 0 ) {
- tr.viewDef->maxDrawSurfs = INITIAL_DRAWSURFS;
- count = 0;
- } else {
- count = tr.viewDef->maxDrawSurfs * sizeof( tr.viewDef->drawSurfs[0] );
- tr.viewDef->maxDrawSurfs *= 2;
- }
- tr.viewDef->drawSurfs = (drawSurf_t **)R_FrameAlloc( tr.viewDef->maxDrawSurfs * sizeof( tr.viewDef->drawSurfs[0] ) );
- memcpy( tr.viewDef->drawSurfs, old, count );
- }
- tr.viewDef->drawSurfs[tr.viewDef->numDrawSurfs] = drawSurf;
- tr.viewDef->numDrawSurfs++;
- // process the shader expressions for conditionals / color / texcoords
- const float *constRegs = shader->ConstantRegisters();
- if ( constRegs ) {
- // shader only uses constant values
- drawSurf->shaderRegisters = constRegs;
- } else {
- float *regs = (float *)R_FrameAlloc( shader->GetNumRegisters() * sizeof( float ) );
- drawSurf->shaderRegisters = regs;
- // a reference shader will take the calculated stage color value from another shader
- // and use that for the parm0-parm3 of the current shader, which allows a stage of
- // a light model and light flares to pick up different flashing tables from
- // different light shaders
- if ( renderEntity->referenceShader ) {
- // evaluate the reference shader to find our shader parms
- const shaderStage_t *pStage;
- renderEntity->referenceShader->EvaluateRegisters( refRegs, renderEntity->shaderParms, tr.viewDef, renderEntity->referenceSound );
- pStage = renderEntity->referenceShader->GetStage(0);
- memcpy( generatedShaderParms, renderEntity->shaderParms, sizeof( generatedShaderParms ) );
- generatedShaderParms[0] = refRegs[ pStage->color.registers[0] ];
- generatedShaderParms[1] = refRegs[ pStage->color.registers[1] ];
- generatedShaderParms[2] = refRegs[ pStage->color.registers[2] ];
- shaderParms = generatedShaderParms;
- } else {
- // evaluate with the entityDef's shader parms
- shaderParms = renderEntity->shaderParms;
- }
- float oldFloatTime;
- int oldTime;
- if ( space->entityDef && space->entityDef->parms.timeGroup ) {
- oldFloatTime = tr.viewDef->floatTime;
- oldTime = tr.viewDef->renderView.time;
- tr.viewDef->floatTime = game->GetTimeGroupTime( space->entityDef->parms.timeGroup ) * 0.001;
- tr.viewDef->renderView.time = game->GetTimeGroupTime( space->entityDef->parms.timeGroup );
- }
- shader->EvaluateRegisters( regs, shaderParms, tr.viewDef, renderEntity->referenceSound );
- if ( space->entityDef && space->entityDef->parms.timeGroup ) {
- tr.viewDef->floatTime = oldFloatTime;
- tr.viewDef->renderView.time = oldTime;
- }
- }
- // check for deformations
- R_DeformDrawSurf( drawSurf );
- // skybox surfaces need a dynamic texgen
- switch( shader->Texgen() ) {
- case TG_SKYBOX_CUBE:
- R_SkyboxTexGen( drawSurf, tr.viewDef->renderView.vieworg );
- break;
- case TG_WOBBLESKY_CUBE:
- R_WobbleskyTexGen( drawSurf, tr.viewDef->renderView.vieworg );
- break;
- }
- // check for gui surfaces
- idUserInterface *gui = NULL;
- if ( !space->entityDef ) {
- gui = shader->GlobalGui();
- } else {
- int guiNum = shader->GetEntityGui() - 1;
- if ( guiNum >= 0 && guiNum < MAX_RENDERENTITY_GUI ) {
- gui = renderEntity->gui[ guiNum ];
- }
- if ( gui == NULL ) {
- gui = shader->GlobalGui();
- }
- }
- if ( gui ) {
- // force guis on the fast time
- float oldFloatTime;
- int oldTime;
- oldFloatTime = tr.viewDef->floatTime;
- oldTime = tr.viewDef->renderView.time;
- tr.viewDef->floatTime = game->GetTimeGroupTime( 1 ) * 0.001;
- tr.viewDef->renderView.time = game->GetTimeGroupTime( 1 );
- idBounds ndcBounds;
- if ( !R_PreciseCullSurface( drawSurf, ndcBounds ) ) {
- // did we ever use this to forward an entity color to a gui that didn't set color?
- // memcpy( tr.guiShaderParms, shaderParms, sizeof( tr.guiShaderParms ) );
- R_RenderGuiSurf( gui, drawSurf );
- }
- tr.viewDef->floatTime = oldFloatTime;
- tr.viewDef->renderView.time = oldTime;
- }
- // we can't add subviews at this point, because that would
- // increment tr.viewCount, messing up the rest of the surface
- // adds for this view
- }
- /*
- ===============
- R_AddAmbientDrawsurfs
- Adds surfaces for the given viewEntity
- Walks through the viewEntitys list and creates drawSurf_t for each surface of
- each viewEntity that has a non-empty scissorRect
- ===============
- */
- static void R_AddAmbientDrawsurfs( viewEntity_t *vEntity ) {
- int i, total;
- idRenderEntityLocal *def;
- srfTriangles_t *tri;
- idRenderModel *model;
- const idMaterial *shader;
- def = vEntity->entityDef;
- if ( def->dynamicModel ) {
- model = def->dynamicModel;
- } else {
- model = def->parms.hModel;
- }
- // add all the surfaces
- total = model->NumSurfaces();
- for ( i = 0 ; i < total ; i++ ) {
- const modelSurface_t *surf = model->Surface( i );
- // for debugging, only show a single surface at a time
- if ( r_singleSurface.GetInteger() >= 0 && i != r_singleSurface.GetInteger() ) {
- continue;
- }
- tri = surf->geometry;
- if ( !tri ) {
- continue;
- }
- if ( !tri->numIndexes ) {
- continue;
- }
- shader = surf->shader;
- shader = R_RemapShaderBySkin( shader, def->parms.customSkin, def->parms.customShader );
- R_GlobalShaderOverride( &shader );
- if ( !shader ) {
- continue;
- }
- if ( !shader->IsDrawn() ) {
- continue;
- }
- // debugging tool to make sure we are have the correct pre-calculated bounds
- if ( r_checkBounds.GetBool() ) {
- int j, k;
- for ( j = 0 ; j < tri->numVerts ; j++ ) {
- for ( k = 0 ; k < 3 ; k++ ) {
- if ( tri->verts[j].xyz[k] > tri->bounds[1][k] + CHECK_BOUNDS_EPSILON
- || tri->verts[j].xyz[k] < tri->bounds[0][k] - CHECK_BOUNDS_EPSILON ) {
- common->Printf( "bad tri->bounds on %s:%s\n", def->parms.hModel->Name(), shader->GetName() );
- break;
- }
- if ( tri->verts[j].xyz[k] > def->referenceBounds[1][k] + CHECK_BOUNDS_EPSILON
- || tri->verts[j].xyz[k] < def->referenceBounds[0][k] - CHECK_BOUNDS_EPSILON ) {
- common->Printf( "bad referenceBounds on %s:%s\n", def->parms.hModel->Name(), shader->GetName() );
- break;
- }
- }
- if ( k != 3 ) {
- break;
- }
- }
- }
- if ( !R_CullLocalBox( tri->bounds, vEntity->modelMatrix, 5, tr.viewDef->frustum ) ) {
- def->visibleCount = tr.viewCount;
- // make sure we have an ambient cache
- if ( !R_CreateAmbientCache( tri, shader->ReceivesLighting() ) ) {
- // don't add anything if the vertex cache was too full to give us an ambient cache
- return;
- }
- // touch it so it won't get purged
- vertexCache.Touch( tri->ambientCache );
- if ( r_useIndexBuffers.GetBool() && !tri->indexCache ) {
- vertexCache.Alloc( tri->indexes, tri->numIndexes * sizeof( tri->indexes[0] ), &tri->indexCache, true );
- }
- if ( tri->indexCache ) {
- vertexCache.Touch( tri->indexCache );
- }
- // add the surface for drawing
- R_AddDrawSurf( tri, vEntity, &vEntity->entityDef->parms, shader, vEntity->scissorRect );
- // ambientViewCount is used to allow light interactions to be rejected
- // if the ambient surface isn't visible at all
- tri->ambientViewCount = tr.viewCount;
- }
- }
- // add the lightweight decal surfaces
- for ( idRenderModelDecal *decal = def->decals; decal; decal = decal->Next() ) {
- decal->AddDecalDrawSurf( vEntity );
- }
- }
- /*
- ==================
- R_CalcEntityScissorRectangle
- ==================
- */
- idScreenRect R_CalcEntityScissorRectangle( viewEntity_t *vEntity ) {
- idBounds bounds;
- idRenderEntityLocal *def = vEntity->entityDef;
- tr.viewDef->viewFrustum.ProjectionBounds( idBox( def->referenceBounds, def->parms.origin, def->parms.axis ), bounds );
- return R_ScreenRectFromViewFrustumBounds( bounds );
- }
- /*
- ===================
- R_AddModelSurfaces
- Here is where dynamic models actually get instantiated, and necessary
- interactions get created. This is all done on a sort-by-model basis
- to keep source data in cache (most likely L2) as any interactions and
- shadows are generated, since dynamic models will typically be lit by
- two or more lights.
- ===================
- */
- void R_AddModelSurfaces( void ) {
- viewEntity_t *vEntity;
- idInteraction *inter, *next;
- idRenderModel *model;
- // clear the ambient surface list
- tr.viewDef->numDrawSurfs = 0;
- tr.viewDef->maxDrawSurfs = 0; // will be set to INITIAL_DRAWSURFS on R_AddDrawSurf
- // go through each entity that is either visible to the view, or to
- // any light that intersects the view (for shadows)
- for ( vEntity = tr.viewDef->viewEntitys; vEntity; vEntity = vEntity->next ) {
- if ( r_useEntityScissors.GetBool() ) {
- // calculate the screen area covered by the entity
- idScreenRect scissorRect = R_CalcEntityScissorRectangle( vEntity );
- // intersect with the portal crossing scissor rectangle
- vEntity->scissorRect.Intersect( scissorRect );
- if ( r_showEntityScissors.GetBool() ) {
- R_ShowColoredScreenRect( vEntity->scissorRect, vEntity->entityDef->index );
- }
- }
- float oldFloatTime;
- int oldTime;
- game->SelectTimeGroup( vEntity->entityDef->parms.timeGroup );
- if ( vEntity->entityDef->parms.timeGroup ) {
- oldFloatTime = tr.viewDef->floatTime;
- oldTime = tr.viewDef->renderView.time;
- tr.viewDef->floatTime = game->GetTimeGroupTime( vEntity->entityDef->parms.timeGroup ) * 0.001;
- tr.viewDef->renderView.time = game->GetTimeGroupTime( vEntity->entityDef->parms.timeGroup );
- }
- if ( tr.viewDef->isXraySubview && vEntity->entityDef->parms.xrayIndex == 1 ) {
- if ( vEntity->entityDef->parms.timeGroup ) {
- tr.viewDef->floatTime = oldFloatTime;
- tr.viewDef->renderView.time = oldTime;
- }
- continue;
- } else if ( !tr.viewDef->isXraySubview && vEntity->entityDef->parms.xrayIndex == 2 ) {
- if ( vEntity->entityDef->parms.timeGroup ) {
- tr.viewDef->floatTime = oldFloatTime;
- tr.viewDef->renderView.time = oldTime;
- }
- continue;
- }
- // add the ambient surface if it has a visible rectangle
- if ( !vEntity->scissorRect.IsEmpty() ) {
- model = R_EntityDefDynamicModel( vEntity->entityDef );
- if ( model == NULL || model->NumSurfaces() <= 0 ) {
- if ( vEntity->entityDef->parms.timeGroup ) {
- tr.viewDef->floatTime = oldFloatTime;
- tr.viewDef->renderView.time = oldTime;
- }
- continue;
- }
- R_AddAmbientDrawsurfs( vEntity );
- tr.pc.c_visibleViewEntities++;
- } else {
- tr.pc.c_shadowViewEntities++;
- }
- //
- // for all the entity / light interactions on this entity, add them to the view
- //
- if ( tr.viewDef->isXraySubview ) {
- if ( vEntity->entityDef->parms.xrayIndex == 2 ) {
- for ( inter = vEntity->entityDef->firstInteraction; inter != NULL && !inter->IsEmpty(); inter = next ) {
- next = inter->entityNext;
- if ( inter->lightDef->viewCount != tr.viewCount ) {
- continue;
- }
- inter->AddActiveInteraction();
- }
- }
- } else {
- // all empty interactions are at the end of the list so once the
- // first is encountered all the remaining interactions are empty
- for ( inter = vEntity->entityDef->firstInteraction; inter != NULL && !inter->IsEmpty(); inter = next ) {
- next = inter->entityNext;
- // skip any lights that aren't currently visible
- // this is run after any lights that are turned off have already
- // been removed from the viewLights list, and had their viewCount cleared
- if ( inter->lightDef->viewCount != tr.viewCount ) {
- continue;
- }
- inter->AddActiveInteraction();
- }
- }
- if ( vEntity->entityDef->parms.timeGroup ) {
- tr.viewDef->floatTime = oldFloatTime;
- tr.viewDef->renderView.time = oldTime;
- }
- }
- }
- /*
- =====================
- R_RemoveUnecessaryViewLights
- =====================
- */
- void R_RemoveUnecessaryViewLights( void ) {
- viewLight_t *vLight;
- // go through each visible light
- for ( vLight = tr.viewDef->viewLights ; vLight ; vLight = vLight->next ) {
- // if the light didn't have any lit surfaces visible, there is no need to
- // draw any of the shadows. We still keep the vLight for debugging
- // draws
- if ( !vLight->localInteractions && !vLight->globalInteractions && !vLight->translucentInteractions ) {
- vLight->localShadows = NULL;
- vLight->globalShadows = NULL;
- }
- }
- if ( r_useShadowSurfaceScissor.GetBool() ) {
- // shrink the light scissor rect to only intersect the surfaces that will actually be drawn.
- // This doesn't seem to actually help, perhaps because the surface scissor
- // rects aren't actually the surface, but only the portal clippings.
- for ( vLight = tr.viewDef->viewLights ; vLight ; vLight = vLight->next ) {
- const drawSurf_t *surf;
- idScreenRect surfRect;
- if ( !vLight->lightShader->LightCastsShadows() ) {
- continue;
- }
- surfRect.Clear();
- for ( surf = vLight->globalInteractions ; surf ; surf = surf->nextOnLight ) {
- surfRect.Union( surf->scissorRect );
- }
- for ( surf = vLight->localShadows ; surf ; surf = surf->nextOnLight ) {
- const_cast<drawSurf_t *>(surf)->scissorRect.Intersect( surfRect );
- }
- for ( surf = vLight->localInteractions ; surf ; surf = surf->nextOnLight ) {
- surfRect.Union( surf->scissorRect );
- }
- for ( surf = vLight->globalShadows ; surf ; surf = surf->nextOnLight ) {
- const_cast<drawSurf_t *>(surf)->scissorRect.Intersect( surfRect );
- }
- for ( surf = vLight->translucentInteractions ; surf ; surf = surf->nextOnLight ) {
- surfRect.Union( surf->scissorRect );
- }
- vLight->scissorRect.Intersect( surfRect );
- }
- }
- }
|