/*
===================
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;
	}
}
Esempio n. 3
0
/*
=================
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 );
		}
	}
}