/* =================== AddAreaEntityRefs Any models that are visible through the current portalStack will have their scissor =================== */ void idRenderWorldLocal::AddAreaEntityRefs(int areaNum, const portalStack_t *ps) { areaReference_t *ref; idRenderEntityLocal *entity; portalArea_t *area; viewEntity_t *vEnt; idBounds b; area = &portalAreas[ areaNum ]; for (ref = area->entityRefs.areaNext ; ref != &area->entityRefs ; ref = ref->areaNext) { entity = ref->entity; // debug tool to allow viewing of only one entity at a time if (r_singleEntity.GetInteger() >= 0 && r_singleEntity.GetInteger() != entity->index) { continue; } // remove decals that are completely faded away R_FreeEntityDefFadedDecals(entity, tr.viewDef->renderView.time); // check for completely suppressing the model if (!r_skipSuppress.GetBool()) { if (entity->parms.suppressSurfaceInViewID && entity->parms.suppressSurfaceInViewID == tr.viewDef->renderView.viewID) { continue; } if (entity->parms.allowSurfaceInViewID && entity->parms.allowSurfaceInViewID != tr.viewDef->renderView.viewID) { continue; } } // cull reference bounds if (CullEntityByPortals(entity, ps)) { // we are culled out through this portal chain, but it might // still be visible through others continue; } vEnt = R_SetEntityDefViewEntity(entity); // possibly expand the scissor rect vEnt->scissorRect.Union(ps->rect); } }
/* ================= R_AddLights ================= */ void R_AddLights() { SCOPED_PROFILE_EVENT( "R_AddLights" ); //------------------------------------------------- // check each light individually, possibly in parallel //------------------------------------------------- if( r_useParallelAddLights.GetBool() ) { for( viewLight_t* vLight = tr.viewDef->viewLights; vLight != NULL; vLight = vLight->next ) { tr.frontEndJobList->AddJob( ( jobRun_t )R_AddSingleLight, vLight ); } tr.frontEndJobList->Submit(); tr.frontEndJobList->Wait(); } else { for( viewLight_t* vLight = tr.viewDef->viewLights; vLight != NULL; vLight = vLight->next ) { R_AddSingleLight( vLight ); } } //------------------------------------------------- // cull lights from the list if they turned out to not be needed //------------------------------------------------- tr.pc.c_viewLights = 0; viewLight_t** ptr = &tr.viewDef->viewLights; while( *ptr != NULL ) { viewLight_t* vLight = *ptr; if( vLight->removeFromList ) { vLight->lightDef->viewCount = -1; // this probably doesn't matter with current code *ptr = vLight->next; continue; } ptr = &vLight->next; // serial work tr.pc.c_viewLights++; for( shadowOnlyEntity_t* shadEnt = vLight->shadowOnlyViewEntities; shadEnt != NULL; shadEnt = shadEnt->next ) { // this will add it to the viewEntities list, but with an empty scissor rect R_SetEntityDefViewEntity( shadEnt->edef ); } if( r_showLightScissors.GetBool() ) { R_ShowColoredScreenRect( vLight->scissorRect, vLight->lightDef->index ); } } //------------------------------------------------- // Add jobs to setup pre-light shadow volumes. //------------------------------------------------- if( r_useParallelAddShadows.GetInteger() == 1 ) { for( viewLight_t* vLight = tr.viewDef->viewLights; vLight != NULL; vLight = vLight->next ) { for( preLightShadowVolumeParms_t* shadowParms = vLight->preLightShadowVolumes; shadowParms != NULL; shadowParms = shadowParms->next ) { tr.frontEndJobList->AddJob( ( jobRun_t )PreLightShadowVolumeJob, shadowParms ); } vLight->preLightShadowVolumes = NULL; } } else { int start = Sys_Microseconds(); for( viewLight_t* vLight = tr.viewDef->viewLights; vLight != NULL; vLight = vLight->next ) { for( preLightShadowVolumeParms_t* shadowParms = vLight->preLightShadowVolumes; shadowParms != NULL; shadowParms = shadowParms->next ) { PreLightShadowVolumeJob( shadowParms ); } vLight->preLightShadowVolumes = NULL; } int end = Sys_Microseconds(); tr.backend.pc.shadowMicroSec += end - start; } }
/* ================= 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 *newInter = 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 *m, modelMatrix[16]; 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 ) ) { newInter->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 ); } } }