/*
==================
idInteraction::CalcInteractionScissorRectangle
==================
*/
idScreenRect idInteraction::CalcInteractionScissorRectangle( const idFrustum &viewFrustum ) {
	idBounds		projectionBounds;
	idScreenRect	portalRect;
	idScreenRect	scissorRect;

	if ( r_useInteractionScissors.GetInteger() == 0 ) {
		return lightDef->viewLight->scissorRect;
	}

	if ( r_useInteractionScissors.GetInteger() < 0 ) {
		// this is the code from Cass at nvidia, it is more precise, but slower
		return R_CalcIntersectionScissor( lightDef, entityDef, tr.viewDef );
	}

	// the following is Mr.E's code

	// frustum must be initialized and valid
	if ( frustumState == idInteraction::FRUSTUM_UNINITIALIZED || frustumState == idInteraction::FRUSTUM_INVALID ) {
		return lightDef->viewLight->scissorRect;
	}

	// calculate scissors for the portals through which the interaction is visible
	if ( r_useInteractionScissors.GetInteger() > 1 ) {
		areaNumRef_t *area;

		if ( frustumState == idInteraction::FRUSTUM_VALID ) {
			// retrieve all the areas the interaction frustum touches
			for ( areaReference_t *ref = entityDef->entityRefs; ref; ref = ref->ownerNext ) {
				area = entityDef->world->areaNumRefAllocator.Alloc();
				area->areaNum = ref->area->areaNum;
				area->next = frustumAreas;
				frustumAreas = area;
			}
			frustumAreas = tr.viewDef->renderWorld->FloodFrustumAreas( frustum, frustumAreas );
			frustumState = idInteraction::FRUSTUM_VALIDAREAS;
		}

		portalRect.Clear();
		for ( area = frustumAreas; area; area = area->next ) {
			portalRect.Union( entityDef->world->GetAreaScreenRect( area->areaNum ) );
		}
		portalRect.Intersect( lightDef->viewLight->scissorRect );
	} else {
		portalRect = lightDef->viewLight->scissorRect;
	}

	// early out if the interaction is not visible through any portals
	if ( portalRect.IsEmpty() ) {
		return portalRect;
	}

	// calculate bounds of the interaction frustum projected into the view frustum
	if ( lightDef->parms.pointLight ) {
		viewFrustum.ClippedProjectionBounds( frustum, idBox( lightDef->parms.origin, lightDef->parms.lightRadius, lightDef->parms.axis ), projectionBounds );
	} else {
		viewFrustum.ClippedProjectionBounds( frustum, idBox( lightDef->frustumTris->bounds ), projectionBounds );
	}

	if ( projectionBounds.IsCleared() ) {
		return portalRect;
	}

	// derive a scissor rectangle from the projection bounds
	scissorRect = R_ScreenRectFromViewFrustumBounds( projectionBounds );

	// intersect with the portal crossing scissor rectangle
	scissorRect.Intersect( portalRect );

	if ( r_showInteractionScissors.GetInteger() > 0 ) {
		R_ShowColoredScreenRect( scissorRect, lightDef->index );
	}

	return scissorRect;
}
示例#2
0
/*
=================
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;
				}
				
				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 );
			}
		}
		
		// 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 )
			{
				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 );
		}
	}
}
/*
=================
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;
	}
}
示例#4
0
/*
===================
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;
		}
	}
}