示例#1
0
/*
========================
R_XrayViewBySurface
========================
*/
static viewDef_t *R_XrayViewBySurface( drawSurf_t *drawSurf ) {
    viewDef_t		*parms;
    orientation_t	surface, camera;
    idPlane			originalPlane, plane;

    // copy the viewport size from the original
    parms = (viewDef_t *)R_FrameAlloc( sizeof( *parms ) );
    *parms = *tr.viewDef;
    parms->renderView.viewID = 0;	// clear to allow player bodies to show up, and suppress view weapons

    parms->isSubview = true;
    parms->isXraySubview = true;

    return parms;
}
/*
===================
idRenderWorldLocal::BuildConnectedAreas

This is only valid for a given view, not all views in a frame
===================
*/
void idRenderWorldLocal::BuildConnectedAreas() {
	tr.viewDef->connectedAreas = (bool *)R_FrameAlloc( numPortalAreas * sizeof( tr.viewDef->connectedAreas[0] ) );

	// if we are outside the world, we can see all areas
	if ( tr.viewDef->areaNum == -1 ) {
		for ( int i = 0; i < numPortalAreas; i++ ) {
			tr.viewDef->connectedAreas[i] = true;
		}
		return;
	}

	// start with none visible, and flood fill from the current area
	memset( tr.viewDef->connectedAreas, 0, numPortalAreas * sizeof( tr.viewDef->connectedAreas[0] ) );
	BuildConnectedAreas_r( tr.viewDef->areaNum );
}
示例#3
0
/*
========================
R_MirrorViewBySurface
========================
*/
static viewDef_t *R_MirrorViewBySurface( drawSurf_t *drawSurf ) {
	viewDef_t		*parms;
	orientation_t	surface, camera;
	idPlane			originalPlane, plane;

	// copy the viewport size from the original
	parms = (viewDef_t *)R_FrameAlloc( sizeof( *parms ) );
	*parms = *tr.viewDef;
	parms->renderView.viewID = 0;	// clear to allow player bodies to show up, and suppress view weapons

	parms->isSubview = true;
	parms->isMirror = true;

	// create plane axis for the portal we are seeing
	R_PlaneForSurface( drawSurf->geo, originalPlane );
	R_LocalPlaneToGlobal( drawSurf->space->modelMatrix, originalPlane, plane );

	surface.origin = plane.Normal() * -plane[3];
	surface.axis[0] = plane.Normal();
	surface.axis[0].NormalVectors( surface.axis[1], surface.axis[2] );
	surface.axis[2] = -surface.axis[2];

	camera.origin = surface.origin;
	camera.axis[0] = -surface.axis[0];
	camera.axis[1] = surface.axis[1];
	camera.axis[2] = surface.axis[2];

	// set the mirrored origin and axis
	R_MirrorPoint( tr.viewDef->renderView.vieworg, &surface, &camera, parms->renderView.vieworg );

	R_MirrorVector( tr.viewDef->renderView.viewaxis[0], &surface, &camera, parms->renderView.viewaxis[0] );
	R_MirrorVector( tr.viewDef->renderView.viewaxis[1], &surface, &camera, parms->renderView.viewaxis[1] );
	R_MirrorVector( tr.viewDef->renderView.viewaxis[2], &surface, &camera, parms->renderView.viewaxis[2] );

	// make the view origin 16 units away from the center of the surface
	idVec3	viewOrigin = ( drawSurf->geo->bounds[0] + drawSurf->geo->bounds[1] ) * 0.5;
	viewOrigin += ( originalPlane.Normal() * 16 );

	R_LocalPointToGlobal( drawSurf->space->modelMatrix, viewOrigin, parms->initialViewAreaOrigin );

	// set the mirror clip plane
	parms->numClipPlanes = 1;
	parms->clipPlanes[0] = -camera.axis[0];

	parms->clipPlanes[0][3] = -( camera.origin * parms->clipPlanes[0].Normal() );
	
	return parms;
}
/*
=====================
idRenderModelDecal::AddDecalDrawSurf
=====================
*/
void idRenderModelDecal::AddDecalDrawSurf( viewEntity_t *space ) {
	int i, j, maxTime;
	float f;
	decalInfo_t	decalInfo;
	if( tri.numIndexes == 0 ) {
		return;
	}
	// fade down all the verts with time
	decalInfo = material->GetDecalInfo();
	maxTime = decalInfo.stayTime + decalInfo.fadeTime;
	// set vertex colors and remove faded triangles
	for( i = 0 ; i < tri.numIndexes ; i += 3 ) {
		int	deltaTime = tr.viewDef->renderView.time - indexStartTime[i];
		if( deltaTime > maxTime ) {
			continue;
		}
		if( deltaTime <= decalInfo.stayTime ) {
			continue;
		}
		deltaTime -= decalInfo.stayTime;
		f = ( float )deltaTime / decalInfo.fadeTime;
		for( j = 0; j < 3; j++ ) {
			int	ind = tri.indexes[i + j];
			for( int k = 0; k < 4; k++ ) {
				float fcolor = decalInfo.start[k] + ( decalInfo.end[k] - decalInfo.start[k] ) * f;
				int icolor = idMath::FtoiFast( fcolor * vertDepthFade[ind] * 255.0f );
				if( icolor < 0 ) {
					icolor = 0;
				} else if( icolor > 255 ) {
					icolor = 255;
				}
				tri.verts[ind].color[k] = icolor;
			}
		}
	}
	// copy the tri and indexes to temp heap memory,
	// because if we are running multi-threaded, we wouldn't
	// be able to reorganize the index list
	srfTriangles_t *newTri = ( srfTriangles_t * )R_FrameAlloc( sizeof( *newTri ) );
	*newTri = tri;
	// copy the current vertexes to temp vertex cache
	newTri->ambientCache = vertexCache.AllocFrameTemp( tri.verts, tri.numVerts * sizeof( idDrawVert ) );
	// create the drawsurf
	R_AddDrawSurf( newTri, space, &space->entityDef->parms, material, space->scissorRect );
}
示例#5
0
/*
===============
R_RemoteRender
===============
*/
static void R_RemoteRender( drawSurf_t *surf, textureStage_t *stage ) {
	viewDef_t		*parms;
	// remote views can be reused in a single frame
	if( stage->dynamicFrameCount == tr.frameCount ) {
		return;
	}
	// if the entity doesn't have a remoteRenderView, do nothing
	if( !surf->space->entityDef->parms.remoteRenderView ) {
		return;
	}
	// copy the viewport size from the original
	parms = ( viewDef_t * )R_FrameAlloc( sizeof( *parms ) );
	*parms = *tr.viewDef;
	parms->isSubview = true;
	parms->isMirror = false;
	parms->renderView = *surf->space->entityDef->parms.remoteRenderView;
	parms->renderView.viewID = 0;	// clear to allow player bodies to show up, and suppress view weapons
	parms->initialViewAreaOrigin = parms->renderView.vieworg;
	tr.CropRenderSize( stage->width, stage->height, true );
	parms->renderView.x = 0;
	parms->renderView.y = 0;
	parms->renderView.width = SCREEN_WIDTH;
	parms->renderView.height = SCREEN_HEIGHT;
	tr.RenderViewToViewport( &parms->renderView, &parms->viewport );
	parms->scissor.x1 = 0;
	parms->scissor.y1 = 0;
	parms->scissor.x2 = parms->viewport.x2 - parms->viewport.x1;
	parms->scissor.y2 = parms->viewport.y2 - parms->viewport.y1;
	parms->superView = tr.viewDef;
	parms->subviewSurface = surf;
	// generate render commands for it
	R_RenderView( parms );
	// copy this rendering to the image
	stage->dynamicFrameCount = tr.frameCount;
	if( !stage->image ) {
		stage->image = globalImages->scratchImage;
	}
	tr.CaptureRenderToImage( stage->image->imgName );
	tr.UnCrop();
}
/*
====================
R_ToggleSmpFrame
====================
*/
void R_ToggleSmpFrame()
{
	// update the highwater mark
	if( frameData->frameMemoryAllocated.GetValue() > frameData->highWaterAllocated )
	{
		frameData->highWaterAllocated = frameData->frameMemoryAllocated.GetValue();
#if defined( TRACK_FRAME_ALLOCS )
		frameData->highWaterUsed = frameData->frameMemoryUsed.GetValue();
		for( int i = 0; i < FRAME_ALLOC_MAX; i++ )
		{
			frameHighWaterTypeCount[i] = frameAllocTypeCount[i].GetValue();
		}
#endif
	}
	
	// switch to the next frame
	smpFrame++;
	frameData = &smpFrameData[smpFrame % NUM_FRAME_DATA];
	
	// reset the memory allocation
	
	// RB: 64 bit fixes, changed unsigned int to uintptr_t
	const uintptr_t bytesNeededForAlignment = FRAME_ALLOC_ALIGNMENT - ( ( uintptr_t )frameData->frameMemory & ( FRAME_ALLOC_ALIGNMENT - 1 ) );
	// RB end
	
	frameData->frameMemoryAllocated.SetValue( bytesNeededForAlignment );
	frameData->frameMemoryUsed.SetValue( 0 );
	
#if defined( TRACK_FRAME_ALLOCS )
	for( int i = 0; i < FRAME_ALLOC_MAX; i++ )
	{
		frameAllocTypeCount[i].SetValue( 0 );
	}
#endif
	
	// clear the command chain and make a RC_NOP command the only thing on the list
	frameData->cmdHead = frameData->cmdTail = ( emptyCommand_t* )R_FrameAlloc( sizeof( *frameData->cmdHead ), FRAME_ALLOC_DRAW_COMMAND );
	frameData->cmdHead->commandId = RC_NOP;
	frameData->cmdHead->next = NULL;
}
/*
===================
R_AddSingleLight

May be run in parallel.

Sets vLight->removeFromList to true if the light should be removed from the list.
Builds a chain of entities that need to be added for shadows only off vLight->shadowOnlyViewEntities.
Allocates and fills in vLight->entityInteractionState.

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.

Add any precomputed shadow volumes.
===================
*/
static void R_AddSingleLight( viewLight_t* vLight )
{
	// until proven otherwise
	vLight->removeFromList = true;
	vLight->shadowOnlyViewEntities = NULL;
	vLight->preLightShadowVolumes = NULL;
	
	// globals we really should pass in...
	const viewDef_t* viewDef = tr.viewDef;
	
	const idRenderLightLocal* light = vLight->lightDef;
	const idMaterial* lightShader = light->lightShader;
	if( lightShader == NULL )
	{
		common->Error( "R_AddSingleLight: NULL lightShader" );
		return;
	}
	
	SCOPED_PROFILE_EVENT( lightShader->GetName() );
	
	// see if we are suppressing the light in this view
	if( !r_skipSuppress.GetBool() )
	{
		if( light->parms.suppressLightInViewID && light->parms.suppressLightInViewID == viewDef->renderView.viewID )
		{
			return;
		}
		if( light->parms.allowLightInViewID && light->parms.allowLightInViewID != viewDef->renderView.viewID )
		{
			return;
		}
	}
	
	// evaluate the light shader registers
	float* lightRegs = ( float* )R_FrameAlloc( lightShader->GetNumRegisters() * sizeof( float ), FRAME_ALLOC_SHADER_REGISTER );
	lightShader->EvaluateRegisters( lightRegs, light->parms.shaderParms, viewDef->renderView.shaderParms,
									tr.viewDef->renderView.time[0] * 0.001f, 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
			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
			return;
		}
	}
	
	
	//--------------------------------------------
	// 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];
	
	// the fog plane is the light far clip plane
	idPlane fogPlane(	light->baseLightProject[2][0] - light->baseLightProject[3][0],
						light->baseLightProject[2][1] - light->baseLightProject[3][1],
						light->baseLightProject[2][2] - light->baseLightProject[3][2],
						light->baseLightProject[2][3] - light->baseLightProject[3][3] );
	const float planeScale = idMath::InvSqrt( fogPlane.Normal().LengthSqr() );
	vLight->fogPlane[0] = fogPlane[0] * planeScale;
	vLight->fogPlane[1] = fogPlane[1] * planeScale;
	vLight->fogPlane[2] = fogPlane[2] * planeScale;
	vLight->fogPlane[3] = fogPlane[3] * planeScale;
	
	// copy the matrix for deforming the 'zeroOneCubeModel' to exactly cover the light volume in world space
	vLight->inverseBaseLightProject = light->inverseBaseLightProject;
	
	// RB begin
	vLight->baseLightProject = light->baseLightProject;
	vLight->pointLight = light->parms.pointLight;
	vLight->parallel = light->parms.parallel;
	vLight->lightCenter = light->parms.lightCenter;
	// RB end
	
	vLight->falloffImage = light->falloffImage;
	vLight->lightShader = light->lightShader;
	vLight->shaderRegisters = lightRegs;
	
	const bool lightCastsShadows = light->LightCastsShadows();
	
	if( r_useLightScissors.GetInteger() != 0 )
	{
		// Calculate the matrix that projects the zero-to-one cube to exactly cover the
		// light frustum in clip space.
		idRenderMatrix invProjectMVPMatrix;
		idRenderMatrix::Multiply( viewDef->worldSpace.mvp, light->inverseBaseLightProject, invProjectMVPMatrix );
		
		// Calculate the projected bounds, either not clipped at all, near clipped, or fully clipped.
		idBounds projected;
		if( r_useLightScissors.GetInteger() == 1 )
		{
			idRenderMatrix::ProjectedBounds( projected, invProjectMVPMatrix, bounds_zeroOneCube );
		}
		else if( r_useLightScissors.GetInteger() == 2 )
		{
			idRenderMatrix::ProjectedNearClippedBounds( projected, invProjectMVPMatrix, bounds_zeroOneCube );
		}
		else
		{
			idRenderMatrix::ProjectedFullyClippedBounds( projected, invProjectMVPMatrix, bounds_zeroOneCube );
		}
		
		if( projected[0][2] >= projected[1][2] )
		{
			// the light was culled to the view frustum
			return;
		}
		
		float screenWidth = ( float )viewDef->viewport.x2 - ( float )viewDef->viewport.x1;
		float screenHeight = ( float )viewDef->viewport.y2 - ( float )viewDef->viewport.y1;
		
		idScreenRect lightScissorRect;
		lightScissorRect.x1 = idMath::Ftoi( projected[0][0] * screenWidth );
		lightScissorRect.x2 = idMath::Ftoi( projected[1][0] * screenWidth );
		lightScissorRect.y1 = idMath::Ftoi( projected[0][1] * screenHeight );
		lightScissorRect.y2 = idMath::Ftoi( projected[1][1] * screenHeight );
		lightScissorRect.Expand();
		
		vLight->scissorRect.Intersect( lightScissorRect );
		vLight->scissorRect.zmin = projected[0][2];
		vLight->scissorRect.zmax = projected[1][2];
		
		// RB: calculate shadow LOD similar to Q3A .md3 LOD code
		vLight->shadowLOD = 0;
		
		if( r_useShadowMapping.GetBool() && lightCastsShadows )
		{
			float           flod, lodscale;
			float           projectedRadius;
			int             lod;
			int             numLods;
			
			numLods = MAX_SHADOWMAP_RESOLUTIONS;
			
			// compute projected bounding sphere
			// and use that as a criteria for selecting LOD
			idVec3 center = projected.GetCenter();
			projectedRadius = projected.GetRadius( center );
			if( projectedRadius > 1.0f )
			{
				projectedRadius = 1.0f;
			}
			
			if( projectedRadius != 0 )
			{
				lodscale = r_shadowMapLodScale.GetFloat();
				
				if( lodscale > 20 )
					lodscale = 20;
					
				flod = 1.0f - projectedRadius * lodscale;
			}
			else
			{
				// object intersects near view plane, e.g. view weapon
				flod = 0;
			}
			
			flod *= numLods;
			
			if( flod < 0 )
			{
				flod = 0;
			}
			
			lod = idMath::Ftoi( flod );
			
			if( lod >= numLods )
			{
				//lod = numLods - 1;
			}
			
			lod += r_shadowMapLodBias.GetInteger();
			
			if( lod < 0 )
			{
				lod = 0;
			}
			
			if( lod >= numLods )
			{
				// don't draw any shadow
				//lod = -1;
				
				lod = numLods - 1;
			}
			
			// 2048^2 ultra quality is only for cascaded shadow mapping with sun lights
			if( lod == 0 && !light->parms.parallel )
			{
				lod = 1;
			}
			
			vLight->shadowLOD = lod;
		}
		// RB end
	}
	
	// this one stays on the list
	vLight->removeFromList = false;
	
	//--------------------------------------------
	// 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
	//--------------------------------------------
	const int renderViewID = viewDef->renderView.viewID;
	
	// this bool array will be set true whenever the entity will visibly interact with the light
	vLight->entityInteractionState = ( byte* )R_ClearedFrameAlloc( light->world->entityDefs.Num() * sizeof( vLight->entityInteractionState[0] ), FRAME_ALLOC_INTERACTION_STATE );
	
	idInteraction** const interactionTableRow = light->world->interactionTable + light->index * light->world->interactionTableWidth;
	
	for( areaReference_t* lref = light->references; lref != NULL; lref = lref->ownerNext )
	{
		portalArea_t* area = lref->area;
		
		// some lights have their center of projection outside the world, but otherwise
		// we want to ignore areas that are not connected to the light center due to a closed door
		if( light->areaNum != -1 && r_useAreasConnectedForShadowCulling.GetInteger() == 2 )
		{
			if( !light->world->AreasAreConnected( light->areaNum, area->areaNum, PS_BLOCK_VIEW ) )
			{
				// can't possibly be seen or shadowed
				continue;
			}
		}
		
		// check all the models in this area
		for( areaReference_t* eref = area->entityRefs.areaNext; eref != &area->entityRefs; eref = eref->areaNext )
		{
			idRenderEntityLocal* edef = eref->entity;
			
			if( vLight->entityInteractionState[ edef->index ] != viewLight_t::INTERACTION_UNCHECKED )
			{
				continue;
			}
			// until proven otherwise
			vLight->entityInteractionState[ edef->index ] = viewLight_t::INTERACTION_NO;
			
			// The table is updated at interaction::AllocAndLink() and interaction::UnlinkAndFree()
			const idInteraction* inter = interactionTableRow[ edef->index ];
			
			const renderEntity_t& eParms = edef->parms;
			const idRenderModel* eModel = eParms.hModel;
			
			// a large fraction of static entity / light pairs will still have no interactions even though
			// they are both present in the same area(s)
			if( eModel != NULL && !eModel->IsDynamicModel() && inter == INTERACTION_EMPTY )
			{
				// the interaction was statically checked, and it didn't generate any surfaces,
				// so there is no need to force the entity onto the view list if it isn't
				// already there
				continue;
			}
			
			// We don't want the lights on weapons to illuminate anything else.
			// There are two assumptions here -- that allowLightInViewID is only
			// used for weapon lights, and that all weapons will have weaponDepthHack.
			// A more general solution would be to have an allowLightOnEntityID field.
			// HACK: the armor-mounted flashlight is a private spot light, which is probably
			// wrong -- you would expect to see them in multiplayer.
			//	if( light->parms.allowLightInViewID && light->parms.pointLight && !eParms.weaponDepthHack )
			//	{
			//		continue;
			//	}
			
			// non-shadow casting entities don't need to be added if they aren't
			// directly visible
			if( ( eParms.noShadow || ( eModel && !eModel->ModelHasShadowCastingSurfaces() ) ) && !edef->IsDirectlyVisible() )
			{
				continue;
			}
			
			// if the model doesn't accept lighting or cast shadows, it doesn't need to be added
			if( eModel && !eModel->ModelHasInteractingSurfaces() && !eModel->ModelHasShadowCastingSurfaces() )
			{
				continue;
			}
			
			// no interaction present, so either the light or entity has moved
			// assert( lightHasMoved || edef->entityHasMoved );
			if( inter == NULL )
			{
				// 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( eParms.noDynamicInteractions )
				{
					continue;
				}
				
				// do a check of the entity reference bounds against the light frustum to see if they can't
				// possibly interact, despite sharing one or more world areas
				if( R_CullModelBoundsToLight( light, edef->localReferenceBounds, edef->modelRenderMatrix ) )
				{
					continue;
				}
			}
			
			// we now know that the entity and light do overlap
			
			if( edef->IsDirectlyVisible() )
			{
				// entity is directly visible, so the interaction is definitely needed
				vLight->entityInteractionState[ edef->index ] = viewLight_t::INTERACTION_YES;
				continue;
			}
			
			// the entity is not directly visible, but if we can tell that it may cast
			// shadows onto visible surfaces, we must make a viewEntity for it
			if( !lightCastsShadows )
			{
				// surfaces are never shadowed in this light
				continue;
			}
			// if we are suppressing its shadow in this view (player shadows, etc), skip
			if( !r_skipSuppress.GetBool() )
			{
				if( eParms.suppressShadowInViewID && eParms.suppressShadowInViewID == renderViewID )
				{
					continue;
				}
				if( eParms.suppressShadowInLightID && eParms.suppressShadowInLightID == light->parms.lightId )
				{
					continue;
				}
			}
			
			// should we use the shadow bounds from pre-calculated interactions?
			idBounds shadowBounds;
			R_ShadowBounds( edef->globalReferenceBounds, light->globalLightBounds, light->globalLightOrigin, shadowBounds );
			
			// this test is pointless if we knew the light was completely contained
			// in the view frustum, but the entity would also be directly visible in most
			// of those cases.
			
			// this doesn't say that the shadow can't effect anything, only that it can't
			// effect anything in the view, so we shouldn't set up a view entity
			if( idRenderMatrix::CullBoundsToMVP( viewDef->worldSpace.mvp, shadowBounds ) )
			{
				continue;
			}
			
			// debug tool to allow viewing of only one entity at a time
			if( r_singleEntity.GetInteger() >= 0 && r_singleEntity.GetInteger() != edef->index )
			{
				continue;
			}
			
			// we do need it for shadows
			vLight->entityInteractionState[ edef->index ] = viewLight_t::INTERACTION_YES;
			
			// we will need to create a viewEntity_t for it in the serial code section
			shadowOnlyEntity_t* shadEnt = ( shadowOnlyEntity_t* )R_FrameAlloc( sizeof( shadowOnlyEntity_t ), FRAME_ALLOC_SHADOW_ONLY_ENTITY );
			shadEnt->next = vLight->shadowOnlyViewEntities;
			shadEnt->edef = edef;
			vLight->shadowOnlyViewEntities = shadEnt;
		}
	}
	
	//--------------------------------------------
	// add the prelight shadows for the static world geometry
	//--------------------------------------------
	if( light->parms.prelightModel != NULL && !r_useShadowMapping.GetBool() )
	{
		srfTriangles_t* tri = light->parms.prelightModel->Surface( 0 )->geometry;
		
		// these shadows will have valid bounds, and can be culled normally,
		// but they will typically cover most of the light's bounds
		if( idRenderMatrix::CullBoundsToMVP( viewDef->worldSpace.mvp, tri->bounds ) )
		{
			return;
		}
		
		// prelight models should always have static data that never gets purged
		assert( vertexCache.CacheIsCurrent( tri->shadowCache ) );
		assert( vertexCache.CacheIsCurrent( tri->indexCache ) );
		
		drawSurf_t* shadowDrawSurf = ( drawSurf_t* )R_FrameAlloc( sizeof( *shadowDrawSurf ), FRAME_ALLOC_DRAW_SURFACE );
		
		shadowDrawSurf->frontEndGeo = tri;
		shadowDrawSurf->ambientCache = 0;
		shadowDrawSurf->indexCache = tri->indexCache;
		shadowDrawSurf->shadowCache = tri->shadowCache;
		shadowDrawSurf->jointCache = 0;
		shadowDrawSurf->numIndexes = 0;
		shadowDrawSurf->space = &viewDef->worldSpace;
		shadowDrawSurf->material = NULL;
		shadowDrawSurf->extraGLState = 0;
		shadowDrawSurf->shaderRegisters = NULL;
		shadowDrawSurf->scissorRect = vLight->scissorRect;		// default to the light scissor and light depth bounds
		shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_DONE;	// assume the shadow volume is done in case r_skipPrelightShadows is set
		
		if( !r_skipPrelightShadows.GetBool() )
		{
			preLightShadowVolumeParms_t* shadowParms = ( preLightShadowVolumeParms_t* )R_FrameAlloc( sizeof( shadowParms[0] ), FRAME_ALLOC_SHADOW_VOLUME_PARMS );
			
			shadowParms->verts = tri->preLightShadowVertexes;
			shadowParms->numVerts = tri->numVerts * 2;
			shadowParms->indexes = tri->indexes;
			shadowParms->numIndexes = tri->numIndexes;
			shadowParms->triangleBounds = tri->bounds;
			shadowParms->triangleMVP = viewDef->worldSpace.mvp;
			shadowParms->localLightOrigin = vLight->globalLightOrigin;
			shadowParms->localViewOrigin = viewDef->renderView.vieworg;
			shadowParms->zNear = r_znear.GetFloat();
			shadowParms->lightZMin = vLight->scissorRect.zmin;
			shadowParms->lightZMax = vLight->scissorRect.zmax;
			shadowParms->forceShadowCaps = r_forceShadowCaps.GetBool();
			shadowParms->useShadowPreciseInsideTest = r_useShadowPreciseInsideTest.GetBool();
			shadowParms->useShadowDepthBounds = r_useShadowDepthBounds.GetBool();
			shadowParms->numShadowIndices = & shadowDrawSurf->numIndexes;
			shadowParms->renderZFail = & shadowDrawSurf->renderZFail;
			shadowParms->shadowZMin = & shadowDrawSurf->scissorRect.zmin;
			shadowParms->shadowZMax = & shadowDrawSurf->scissorRect.zmax;
			shadowParms->shadowVolumeState = & shadowDrawSurf->shadowVolumeState;
			
			// the pre-light shadow volume "_prelight_light_3297" in "d3xpdm2" is malformed in that it contains the light origin so the precise inside test always fails
			if( tr.primaryWorld->mapName.IcmpPath( "maps/game/mp/d3xpdm2.map" ) == 0 && idStr::Icmp( light->parms.prelightModel->Name(), "_prelight_light_3297" ) == 0 )
			{
				shadowParms->useShadowPreciseInsideTest = false;
			}
			
			shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_UNFINISHED;
			
			shadowParms->next = vLight->preLightShadowVolumes;
			vLight->preLightShadowVolumes = shadowParms;
		}
		
		// actually link it in
		shadowDrawSurf->nextOnLight = vLight->globalShadows;
		vLight->globalShadows = shadowDrawSurf;
	}
}
示例#8
0
/*
=====================
idRenderModelDecal::CreateDecalDrawSurf
=====================
*/
drawSurf_t * idRenderModelDecal::CreateDecalDrawSurf( const viewEntity_t *space, unsigned int index ) {
	if ( index < 0 || index >= numDecalMaterials ) {
		return NULL;
	}

	const idMaterial * material = decalMaterials[index];

	int maxVerts = 0;
	int maxIndexes = 0;
	for ( unsigned int i = firstDecal; i < nextDecal; i++ ) {
		const decal_t & decal = decals[i & ( MAX_DECALS - 1 )];
		if ( decal.material == material ) {
			maxVerts += decal.numVerts;
			maxIndexes += decal.numIndexes;
		}
	}

	if ( maxVerts == 0 || maxIndexes == 0 ) {
		return NULL;
	}

	// create a new triangle surface in frame memory so it gets automatically disposed of
	srfTriangles_t *newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES );
	newTri->numVerts = maxVerts;
	newTri->numIndexes = maxIndexes;

	newTri->ambientCache = vertexCache.AllocVertex( NULL, ALIGN( maxVerts * sizeof( idDrawVert ), VERTEX_CACHE_ALIGN ) );
	newTri->indexCache = vertexCache.AllocIndex( NULL, ALIGN( maxIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) );

	idDrawVert * mappedVerts = (idDrawVert *)vertexCache.MappedVertexBuffer( newTri->ambientCache );
	triIndex_t * mappedIndexes = (triIndex_t *)vertexCache.MappedIndexBuffer( newTri->indexCache );

	const decalInfo_t decalInfo = material->GetDecalInfo();
	const int maxTime = decalInfo.stayTime + decalInfo.fadeTime;
	const int time = tr.viewDef->renderView.time[0];

	int numVerts = 0;
	int numIndexes = 0;
	for ( unsigned int i = firstDecal; i < nextDecal; i++ ) {
		const decal_t & decal = decals[i & ( MAX_DECALS - 1 )];

		if ( decal.numVerts == 0 ) {
			if ( i == firstDecal ) {
				firstDecal++;
			}
			continue;
		}

		if ( decal.material != material ) {
			continue;
		}

		const int deltaTime = time - decal.startTime;
		const int fadeTime = deltaTime - decalInfo.stayTime;
		if ( deltaTime > maxTime ) {
			continue;	// already completely faded away, but not yet removed
		}

		const float f = ( deltaTime > decalInfo.stayTime ) ? ( (float) fadeTime / decalInfo.fadeTime ) : 0.0f;

		ALIGNTYPE16 float fadeColor[4];
		for ( int j = 0; j < 4; j++ ) {
			fadeColor[j] = 255.0f * ( decalInfo.start[j] + ( decalInfo.end[j] - decalInfo.start[j] ) * f );
		}

		// use SIMD optimized routine to copy the vertices and indices directly to write-combined memory
		// this also applies any depth/time based fading while copying
		R_CopyDecalSurface( mappedVerts, numVerts, mappedIndexes, numIndexes, &decal, fadeColor );

		numVerts += decal.numVerts;
		numIndexes += decal.numIndexes;
	}
	newTri->numVerts = numVerts;
	newTri->numIndexes = numIndexes;

	// create the drawsurf
	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 = space;
	drawSurf->scissorRect = space->scissorRect;
	drawSurf->extraGLState = 0;
	drawSurf->renderZFail = 0;

	R_SetupDrawSurfShader( drawSurf, material, &space->entityDef->parms );

	return drawSurf;
}
示例#9
0
/*
====================
idRenderModelOverlay::CreateOverlayDrawSurf
====================
*/
drawSurf_t* idRenderModelOverlay::CreateOverlayDrawSurf( const viewEntity_t* space, const idRenderModel* baseModel, unsigned int index )
{
	if( index < 0 || index >= numOverlayMaterials )
	{
		return NULL;
	}
	
	// md5 models won't have any surfaces when r_showSkel is set
	if( baseModel == NULL || baseModel->IsDefaultModel() || baseModel->NumSurfaces() == 0 )
	{
		return NULL;
	}
	
	assert( baseModel->IsDynamicModel() == DM_STATIC );
	
	const idRenderModelStatic* staticModel = static_cast< const idRenderModelStatic* >( baseModel );
	
	const idMaterial* material = overlayMaterials[index];
	
	int maxVerts = 0;
	int maxIndexes = 0;
	for( unsigned int i = firstOverlay; i < nextOverlay; i++ )
	{
		const overlay_t& overlay = overlays[i & ( MAX_OVERLAYS - 1 )];
		if( overlay.material == material )
		{
			maxVerts += overlay.numVerts;
			maxIndexes += overlay.numIndexes;
		}
	}
	
	if( maxVerts == 0 || maxIndexes == 0 )
	{
		return NULL;
	}
	
	// create a new triangle surface in frame memory so it gets automatically disposed of
	srfTriangles_t* newTri = ( srfTriangles_t* )R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES );
	newTri->staticModelWithJoints = ( staticModel->jointsInverted != NULL ) ? const_cast< idRenderModelStatic* >( staticModel ) : NULL;	// allow GPU skinning
	
	newTri->ambientCache = vertexCache.AllocVertex( NULL, ALIGN( maxVerts * sizeof( idDrawVert ), VERTEX_CACHE_ALIGN ) );
	newTri->indexCache = vertexCache.AllocIndex( NULL, ALIGN( maxIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) );
	
	idDrawVert* mappedVerts = ( idDrawVert* )vertexCache.MappedVertexBuffer( newTri->ambientCache );
	triIndex_t* mappedIndexes = ( triIndex_t* )vertexCache.MappedIndexBuffer( newTri->indexCache );
	
	int numVerts = 0;
	int numIndexes = 0;
	
	for( unsigned int i = firstOverlay; i < nextOverlay; i++ )
	{
		overlay_t& overlay = overlays[i & ( MAX_OVERLAYS - 1 )];
		
		if( overlay.numVerts == 0 )
		{
			if( i == firstOverlay )
			{
				firstOverlay++;
			}
			continue;
		}
		
		if( overlay.material != material )
		{
			continue;
		}
		
		// get the source model surface for this overlay surface
		const modelSurface_t* baseSurf = ( overlay.surfaceNum < staticModel->NumSurfaces() ) ? staticModel->Surface( overlay.surfaceNum ) : NULL;
		
		// if the surface ids no longer match
		if( baseSurf == NULL || baseSurf->id != overlay.surfaceId )
		{
			// find the surface with the correct id
			if( staticModel->FindSurfaceWithId( overlay.surfaceId, overlay.surfaceNum ) )
			{
				baseSurf = staticModel->Surface( overlay.surfaceNum );
			}
			else
			{
				// the surface with this id no longer exists
				FreeOverlay( overlay );
				if( i == firstOverlay )
				{
					firstOverlay++;
				}
				continue;
			}
		}
		
		// check for out of range vertex references
		const srfTriangles_t* baseTri = baseSurf->geometry;
		if( overlay.maxReferencedVertex >= baseTri->numVerts )
		{
			// This can happen when playing a demofile and a model has been changed since it was recorded, so just issue a warning and go on.
			common->Warning( "idRenderModelOverlay::CreateOverlayDrawSurf: overlay vertex out of range.  Model has probably changed since generating the overlay." );
			FreeOverlay( overlay );
			if( i == firstOverlay )
			{
				firstOverlay++;
			}
			continue;
		}
		
		// use SIMD optimized routine to copy the vertices and indices directly to write-combined memory
		R_CopyOverlaySurface( mappedVerts, numVerts, mappedIndexes, numIndexes, &overlay, baseTri->verts );
		
		numIndexes += overlay.numIndexes;
		numVerts += overlay.numVerts;
	}
	
	newTri->numVerts = numVerts;
	newTri->numIndexes = numIndexes;
	
	// create the drawsurf
	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->space = space;
	drawSurf->scissorRect = space->scissorRect;
	drawSurf->extraGLState = 0;
	drawSurf->renderZFail = 0;
	
	R_SetupDrawSurfShader( drawSurf, material, &space->entityDef->parms );
	R_SetupDrawSurfJoints( drawSurf, newTri, NULL );
	
	return drawSurf;
}
示例#10
0
/*
================
idGuiModel::EmitFullScreen

Creates a view that covers the screen and emit the surfaces
================
*/
void idGuiModel::EmitFullScreen( void ) {
	viewDef_t	*viewDef;

	if ( surfaces[0].numVerts == 0 ) {
		return;
	}

	viewDef = (viewDef_t *)R_ClearedFrameAlloc( sizeof( *viewDef ) );

	// for gui editor
	if ( !tr.viewDef || !tr.viewDef->isEditor ) {
		viewDef->renderView.x = 0;
		viewDef->renderView.y = 0;
		viewDef->renderView.width = SCREEN_WIDTH;
		viewDef->renderView.height = SCREEN_HEIGHT;

		tr.RenderViewToViewport( &viewDef->renderView, &viewDef->viewport );

		viewDef->scissor.x1 = 0;
		viewDef->scissor.y1 = 0;
		viewDef->scissor.x2 = viewDef->viewport.x2 - viewDef->viewport.x1;
		viewDef->scissor.y2 = viewDef->viewport.y2 - viewDef->viewport.y1;
	} else {
		viewDef->renderView.x = tr.viewDef->renderView.x;
		viewDef->renderView.y = tr.viewDef->renderView.y;
		viewDef->renderView.width = tr.viewDef->renderView.width;
		viewDef->renderView.height = tr.viewDef->renderView.height;
		
		viewDef->viewport.x1 = tr.viewDef->renderView.x;
		viewDef->viewport.x2 = tr.viewDef->renderView.x + tr.viewDef->renderView.width;
		viewDef->viewport.y1 = tr.viewDef->renderView.y;
		viewDef->viewport.y2 = tr.viewDef->renderView.y + tr.viewDef->renderView.height;

		viewDef->scissor.x1 = tr.viewDef->scissor.x1;
		viewDef->scissor.y1 = tr.viewDef->scissor.y1;
		viewDef->scissor.x2 = tr.viewDef->scissor.x2;
		viewDef->scissor.y2 = tr.viewDef->scissor.y2;
	}

	viewDef->floatTime = tr.frameShaderTime;

	// qglOrtho( 0, 640, 480, 0, 0, 1 );		// always assume 640x480 virtual coordinates
	viewDef->projectionMatrix[0] = 2.0f / 640.0f;
	viewDef->projectionMatrix[5] = -2.0f / 480.0f;
	viewDef->projectionMatrix[10] = -2.0f / 1.0f;
	viewDef->projectionMatrix[12] = -1.0f;
	viewDef->projectionMatrix[13] = 1.0f;
	viewDef->projectionMatrix[14] = -1.0f;
	viewDef->projectionMatrix[15] = 1.0f;

	viewDef->worldSpace.modelViewMatrix[0] = 1.0f;
	viewDef->worldSpace.modelViewMatrix[5] = 1.0f;
	viewDef->worldSpace.modelViewMatrix[10] = 1.0f;
	viewDef->worldSpace.modelViewMatrix[15] = 1.0f;

	viewDef->maxDrawSurfs = surfaces.Num();
	viewDef->drawSurfs = (drawSurf_t **)R_FrameAlloc( viewDef->maxDrawSurfs * sizeof( viewDef->drawSurfs[0] ) );
	viewDef->numDrawSurfs = 0;

	viewDef_t	*oldViewDef = tr.viewDef;
	tr.viewDef = viewDef;

	// add the surfaces to this view
	for ( int i = 0 ; i < surfaces.Num() ; i++ ) {
		EmitSurface( &surfaces[i], viewDef->worldSpace.modelMatrix, viewDef->worldSpace.modelViewMatrix, false );
	}

	tr.viewDef = oldViewDef;

	// add the command to draw this view
	R_AddDrawViewCmd( viewDef );
}
示例#11
0
/*
====================
R_ClearCommandChain

Called after every buffer submission
and by R_ToggleSmpFrame
====================
*/
void R_ClearCommandChain( void ) {
	// clear the command chain
	frameData->cmdHead = frameData->cmdTail = (emptyCommand_t *)R_FrameAlloc( sizeof( *frameData->cmdHead ) );
	frameData->cmdHead->commandId = RC_NOP;
	frameData->cmdHead->next = NULL;
}
/*
========================
R_MirrorViewBySurface
========================
*/
static viewDef_t* R_MirrorViewBySurface( const drawSurf_t* drawSurf )
{
	// copy the viewport size from the original
	viewDef_t* parms = ( viewDef_t* )R_FrameAlloc( sizeof( *parms ) );
	*parms = *tr.viewDef;
	parms->renderView.viewID = 0;	// clear to allow player bodies to show up, and suppress view weapons
	
	parms->isSubview = true;
	parms->isMirror = true;
	parms->isObliqueProjection = false;
	
	// create plane axis for the portal we are seeing
	idPlane originalPlane, plane;
	R_PlaneForSurface( drawSurf->frontEndGeo, originalPlane );
	R_LocalPlaneToGlobal( drawSurf->space->modelMatrix, originalPlane, plane );
	
	orientation_t surface;
	surface.origin = plane.Normal() * -plane[3];
	surface.axis[0] = plane.Normal();
	surface.axis[0].NormalVectors( surface.axis[1], surface.axis[2] );
	surface.axis[2] = -surface.axis[2];
	
	orientation_t camera;
	camera.origin = surface.origin;
	camera.axis[0] = -surface.axis[0];
	camera.axis[1] = surface.axis[1];
	camera.axis[2] = surface.axis[2];
	
	// set the mirrored origin and axis
	R_MirrorPoint( tr.viewDef->renderView.vieworg, &surface, &camera, parms->renderView.vieworg );
	
	R_MirrorVector( tr.viewDef->renderView.viewaxis[0], &surface, &camera, parms->renderView.viewaxis[0] );
	R_MirrorVector( tr.viewDef->renderView.viewaxis[1], &surface, &camera, parms->renderView.viewaxis[1] );
	R_MirrorVector( tr.viewDef->renderView.viewaxis[2], &surface, &camera, parms->renderView.viewaxis[2] );
	
	// make the view origin 16 units away from the center of the surface
	const idVec3 center = (drawSurf->frontEndGeo->bounds[0] + drawSurf->frontEndGeo->bounds[1]) * 0.5f;
	const idVec3 viewOrigin = center + (originalPlane.Normal() * 16.0f);

	R_LocalPointToGlobal(drawSurf->space->modelMatrix, viewOrigin, parms->initialViewAreaOrigin);

	// set the mirror clip plane
	parms->numClipPlanes = 1;
	parms->clipPlanes[0] = -camera.axis[0];
	
	parms->clipPlanes[0][3] = -( camera.origin * parms->clipPlanes[0].Normal() );
	
	if (r_waterReflectFix.GetBool() && !parms->is2Dgui && drawSurf->material->GetSurfaceType() == SURFTYPE_MIRROR)
	{
		parms->isObliqueProjection = true;
		float dist = parms->clipPlanes[0].Dist();
		float viewdist = parms->renderView.vieworg * parms->clipPlanes[0].Normal();
		float fDist = -dist + viewdist;
		static const float fudge = 2.f;	//fudge avoids depth precision artifacts when performing oblique projection
		if (fDist > fudge || fDist < -fudge)
		{
			if (fDist < 0.f)
				fDist += fudge;
			else
				fDist -= fudge;
		}

		parms->clipPlanes[0][3] = fDist;		

		R_SetupViewMatrix(parms);
		R_SetupProjectionMatrix(parms);
		R_ObliqueProjection(parms);
	}

	return parms;
}
示例#13
0
/*
==================
RB_ShowOverdraw
==================
*/
void RB_ShowOverdraw() {
	const idMaterial *	material;
	int					i;
	drawSurf_t * *		drawSurfs;
	const drawSurf_t *	surf;
	int					numDrawSurfs;
	viewLight_t *		vLight;

	if ( r_showOverDraw.GetInteger() == 0 ) {
		return;
	}

	material = declManager->FindMaterial( "textures/common/overdrawtest", false );
	if ( material == NULL ) {
		return;
	}

	drawSurfs = backEnd.viewDef->drawSurfs;
	numDrawSurfs = backEnd.viewDef->numDrawSurfs;

	int interactions = 0;
	for ( vLight = backEnd.viewDef->viewLights; vLight; vLight = vLight->next ) {
		for ( surf = vLight->localInteractions; surf; surf = surf->nextOnLight ) {
			interactions++;
		}
		for ( surf = vLight->globalInteractions; surf; surf = surf->nextOnLight ) {
			interactions++;
		}
	}

	// FIXME: can't frame alloc from the renderer back-end
	drawSurf_t **newDrawSurfs = (drawSurf_t **)R_FrameAlloc( numDrawSurfs + interactions * sizeof( newDrawSurfs[0] ), FRAME_ALLOC_DRAW_SURFACE_POINTER );

	for ( i = 0; i < numDrawSurfs; i++ ) {
		surf = drawSurfs[i];
		if ( surf->material ) {
			const_cast<drawSurf_t *>(surf)->material = material;
		}
		newDrawSurfs[i] = const_cast<drawSurf_t *>(surf);
	}

	for ( vLight = backEnd.viewDef->viewLights; vLight; vLight = vLight->next ) {
		for ( surf = vLight->localInteractions; surf; surf = surf->nextOnLight ) {
			const_cast<drawSurf_t *>(surf)->material = material;
			newDrawSurfs[i++] = const_cast<drawSurf_t *>(surf);
		}
		for ( surf = vLight->globalInteractions; surf; surf = surf->nextOnLight ) {
			const_cast<drawSurf_t *>(surf)->material = material;
			newDrawSurfs[i++] = const_cast<drawSurf_t *>(surf);
		}
		vLight->localInteractions = NULL;
		vLight->globalInteractions = NULL;
	}

	switch( r_showOverDraw.GetInteger() ) {
		case 1: // geometry overdraw
			const_cast<viewDef_t *>(backEnd.viewDef)->drawSurfs = newDrawSurfs;
			const_cast<viewDef_t *>(backEnd.viewDef)->numDrawSurfs = numDrawSurfs;
			break;
		case 2: // light interaction overdraw
			const_cast<viewDef_t *>(backEnd.viewDef)->drawSurfs = &newDrawSurfs[numDrawSurfs];
			const_cast<viewDef_t *>(backEnd.viewDef)->numDrawSurfs = interactions;
			break;
		case 3: // geometry + light interaction overdraw
			const_cast<viewDef_t *>(backEnd.viewDef)->drawSurfs = newDrawSurfs;
			const_cast<viewDef_t *>(backEnd.viewDef)->numDrawSurfs += interactions;
			break;
	}
}
/*
===================
R_AddSingleModel

May be run in parallel.

Here is where dynamic models actually get instantiated, and necessary
interaction surfaces 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_AddSingleModel( viewEntity_t* vEntity )
{
	// we will add all interaction surfs here, to be chained to the lights in later serial code
	vEntity->drawSurfs = NULL;
	vEntity->staticShadowVolumes = NULL;
	vEntity->dynamicShadowVolumes = NULL;
	
	// globals we really should pass in...
	const viewDef_t* viewDef = tr.viewDef;
	
	idRenderEntityLocal* entityDef = vEntity->entityDef;
	const renderEntity_t* renderEntity = &entityDef->parms;
	const idRenderWorldLocal* world = entityDef->world;
	
	if( viewDef->isXraySubview && entityDef->parms.xrayIndex == 1 )
	{
		return;
	}
	else if( !viewDef->isXraySubview && entityDef->parms.xrayIndex == 2 )
	{
		return;
	}
	
	SCOPED_PROFILE_EVENT( renderEntity->hModel == NULL ? "Unknown Model" : renderEntity->hModel->Name() );
	
	// calculate the znear for testing whether or not the view is inside a shadow projection
	const float znear = ( viewDef->renderView.cramZNear ) ? ( r_znear.GetFloat() * 0.25f ) : r_znear.GetFloat();
	
	// if the entity wasn't seen through a portal chain, it was added just for light shadows
	const bool modelIsVisible = !vEntity->scissorRect.IsEmpty();
	const bool addInteractions = modelIsVisible && ( !viewDef->isXraySubview || entityDef->parms.xrayIndex == 2 );
	const int entityIndex = entityDef->index;
	
	//---------------------------
	// Find which of the visible lights contact this entity
	//
	// If the entity doesn't accept light or cast shadows from any surface,
	// this can be skipped.
	//
	// OPTIMIZE: world areas can assume all referenced lights are used
	//---------------------------
	int	numContactedLights = 0;
	static const int MAX_CONTACTED_LIGHTS = 128;
	viewLight_t* contactedLights[MAX_CONTACTED_LIGHTS];
	idInteraction* staticInteractions[MAX_CONTACTED_LIGHTS];
	
	if( renderEntity->hModel == NULL ||
			renderEntity->hModel->ModelHasInteractingSurfaces() ||
			renderEntity->hModel->ModelHasShadowCastingSurfaces() )
	{
		SCOPED_PROFILE_EVENT( "Find lights" );
		for( viewLight_t* vLight = viewDef->viewLights; vLight != NULL; vLight = vLight->next )
		{
			if( vLight->scissorRect.IsEmpty() )
			{
				continue;
			}
			if( vLight->entityInteractionState != NULL )
			{
				// new code path, everything was done in AddLight
				if( vLight->entityInteractionState[entityIndex] == viewLight_t::INTERACTION_YES )
				{
					contactedLights[numContactedLights] = vLight;
					staticInteractions[numContactedLights] = world->interactionTable[vLight->lightDef->index * world->interactionTableWidth + entityIndex];
					if( ++numContactedLights == MAX_CONTACTED_LIGHTS )
					{
						break;
					}
				}
				continue;
			}
			
			const idRenderLightLocal* lightDef = vLight->lightDef;
			
			if( !lightDef->globalLightBounds.IntersectsBounds( entityDef->globalReferenceBounds ) )
			{
				continue;
			}
			
			if( R_CullModelBoundsToLight( lightDef, entityDef->localReferenceBounds, entityDef->modelRenderMatrix ) )
			{
				continue;
			}
			
			if( !modelIsVisible )
			{
				// some lights have their center of projection outside the world
				if( lightDef->areaNum != -1 )
				{
					// if no part of the model is in an area that is connected to
					// the light center (it is behind a solid, closed door), we can ignore it
					bool areasConnected = false;
					for( areaReference_t* ref = entityDef->entityRefs; ref != NULL; ref = ref->ownerNext )
					{
						if( world->AreasAreConnected( lightDef->areaNum, ref->area->areaNum, PS_BLOCK_VIEW ) )
						{
							areasConnected = true;
							break;
						}
					}
					if( areasConnected == false )
					{
						// can't possibly be seen or shadowed
						continue;
					}
				}
				
				// check more precisely for shadow visibility
				idBounds shadowBounds;
				R_ShadowBounds( entityDef->globalReferenceBounds, lightDef->globalLightBounds, lightDef->globalLightOrigin, shadowBounds );
				
				// this doesn't say that the shadow can't effect anything, only that it can't
				// effect anything in the view
				if( idRenderMatrix::CullBoundsToMVP( viewDef->worldSpace.mvp, shadowBounds ) )
				{
					continue;
				}
			}
			contactedLights[numContactedLights] = vLight;
			staticInteractions[numContactedLights] = world->interactionTable[vLight->lightDef->index * world->interactionTableWidth + entityIndex];
			if( ++numContactedLights == MAX_CONTACTED_LIGHTS )
			{
				break;
			}
		}
	}
	
	// if we aren't visible and none of the shadows stretch into the view,
	// we don't need to do anything else
	if( !modelIsVisible && numContactedLights == 0 )
	{
		return;
	}
	
	//---------------------------
	// create a dynamic model if the geometry isn't static
	//---------------------------
	idRenderModel* model = R_EntityDefDynamicModel( entityDef );
	if( model == NULL || model->NumSurfaces() <= 0 )
	{
		return;
	}
	
	// add the lightweight blood decal surfaces if the model is directly visible
	if( modelIsVisible )
	{
		assert( !vEntity->scissorRect.IsEmpty() );
		
		if( entityDef->decals != NULL && !r_skipDecals.GetBool() )
		{
			entityDef->decals->CreateDeferredDecals( model );
			
			unsigned int numDrawSurfs = entityDef->decals->GetNumDecalDrawSurfs();
			for( unsigned int i = 0; i < numDrawSurfs; i++ )
			{
				drawSurf_t* decalDrawSurf = entityDef->decals->CreateDecalDrawSurf( vEntity, i );
				if( decalDrawSurf != NULL )
				{
					decalDrawSurf->linkChain = NULL;
					decalDrawSurf->nextOnLight = vEntity->drawSurfs;
					vEntity->drawSurfs = decalDrawSurf;
				}
			}
		}
		
		if( entityDef->overlays != NULL && !r_skipOverlays.GetBool() )
		{
			entityDef->overlays->CreateDeferredOverlays( model );
			
			unsigned int numDrawSurfs = entityDef->overlays->GetNumOverlayDrawSurfs();
			for( unsigned int i = 0; i < numDrawSurfs; i++ )
			{
				drawSurf_t* overlayDrawSurf = entityDef->overlays->CreateOverlayDrawSurf( vEntity, model, i );
				if( overlayDrawSurf != NULL )
				{
					overlayDrawSurf->linkChain = NULL;
					overlayDrawSurf->nextOnLight = vEntity->drawSurfs;
					vEntity->drawSurfs = overlayDrawSurf;
				}
			}
		}
	}
	
	//---------------------------
	// copy matrix related stuff for back-end use
	// and setup a render matrix for faster culling
	//---------------------------
	vEntity->modelDepthHack = renderEntity->modelDepthHack;
	vEntity->weaponDepthHack = renderEntity->weaponDepthHack;
	vEntity->skipMotionBlur = renderEntity->skipMotionBlur;
	
	memcpy( vEntity->modelMatrix, entityDef->modelMatrix, sizeof( vEntity->modelMatrix ) );
	R_MatrixMultiply( entityDef->modelMatrix, viewDef->worldSpace.modelViewMatrix, vEntity->modelViewMatrix );
	
	idRenderMatrix viewMat;
	idRenderMatrix::Transpose( *( idRenderMatrix* )vEntity->modelViewMatrix, viewMat );
	idRenderMatrix::Multiply( viewDef->projectionRenderMatrix, viewMat, vEntity->mvp );
	if( renderEntity->weaponDepthHack )
	{
		idRenderMatrix::ApplyDepthHack( vEntity->mvp );
	}
	if( renderEntity->modelDepthHack != 0.0f )
	{
		idRenderMatrix::ApplyModelDepthHack( vEntity->mvp, renderEntity->modelDepthHack );
	}
	
	// local light and view origins are used to determine if the view is definitely outside
	// an extruded shadow volume, which means we can skip drawing the end caps
	idVec3 localViewOrigin;
	R_GlobalPointToLocal( vEntity->modelMatrix, viewDef->renderView.vieworg, localViewOrigin );
	
	//---------------------------
	// add all the model surfaces
	//---------------------------
	for( int surfaceNum = 0; surfaceNum < model->NumSurfaces(); surfaceNum++ )
	{
		const modelSurface_t* surf = model->Surface( surfaceNum );
		
		// for debugging, only show a single surface at a time
		if( r_singleSurface.GetInteger() >= 0 && surfaceNum != r_singleSurface.GetInteger() )
		{
			continue;
		}
		
		srfTriangles_t* tri = surf->geometry;
		if( tri == NULL )
		{
			continue;
		}
		if( tri->numIndexes == 0 )
		{
			continue;		// happens for particles
		}
		const idMaterial* shader = surf->shader;
		if( shader == NULL )
		{
			continue;
		}
		if( !shader->IsDrawn() )
		{
			continue;		// collision hulls, etc
		}
		
		// RemapShaderBySkin
		if( entityDef->parms.customShader != NULL )
		{
			// this is sort of a hack, but causes deformed surfaces to map to empty surfaces,
			// so the item highlight overlay doesn't highlight the autosprite surface
			if( shader->Deform() )
			{
				continue;
			}
			shader = entityDef->parms.customShader;
		}
		else if( entityDef->parms.customSkin )
		{
			shader = entityDef->parms.customSkin->RemapShaderBySkin( shader );
			if( shader == NULL )
			{
				continue;
			}
			if( !shader->IsDrawn() )
			{
				continue;
			}
		}
		
		// optionally override with the renderView->globalMaterial
		if( tr.primaryRenderView.globalMaterial != NULL )
		{
			shader = tr.primaryRenderView.globalMaterial;
		}
		
		SCOPED_PROFILE_EVENT( shader->GetName() );
		
		// debugging tool to make sure we have the correct pre-calculated bounds
		if( r_checkBounds.GetBool() )
		{
			for( int j = 0; j < tri->numVerts; j++ )
			{
				int k;
				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", entityDef->parms.hModel->Name(), shader->GetName() );
						break;
					}
					if( tri->verts[j].xyz[k] > entityDef->localReferenceBounds[1][k] + CHECK_BOUNDS_EPSILON
							|| tri->verts[j].xyz[k] < entityDef->localReferenceBounds[0][k] - CHECK_BOUNDS_EPSILON )
					{
						common->Printf( "bad referenceBounds on %s:%s\n", entityDef->parms.hModel->Name(), shader->GetName() );
						break;
					}
				}
				if( k != 3 )
				{
					break;
				}
			}
		}
		
		// view frustum culling for the precise surface bounds, which is tighter
		// than the entire entity reference bounds
		// If the entire model wasn't visible, there is no need to check the
		// individual surfaces.
		const bool surfaceDirectlyVisible = modelIsVisible && !idRenderMatrix::CullBoundsToMVP( vEntity->mvp, tri->bounds );
		
		// RB: added check wether GPU skinning is available at all
		const bool gpuSkinned = ( tri->staticModelWithJoints != NULL && r_useGPUSkinning.GetBool() && glConfig.gpuSkinningAvailable );
		// RB end
		
		//--------------------------
		// base drawing surface
		//--------------------------
		drawSurf_t* baseDrawSurf = NULL;
		if( surfaceDirectlyVisible )
		{
			// make sure we have an ambient cache and all necessary normals / tangents
			if( !vertexCache.CacheIsCurrent( tri->indexCache ) )
			{
				tri->indexCache = vertexCache.AllocIndex( tri->indexes, ALIGN( tri->numIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) );
			}
			if( !vertexCache.CacheIsCurrent( tri->ambientCache ) )
			{
				// we are going to use it for drawing, so make sure we have the tangents and normals
				if( shader->ReceivesLighting() && !tri->tangentsCalculated )
				{
					assert( tri->staticModelWithJoints == NULL );
					R_DeriveTangents( tri );
					
					// RB: this was hit by parametric particle models ..
					//assert( false );	// this should no longer be hit
					// RB end
				}
				tri->ambientCache = vertexCache.AllocVertex( tri->verts, ALIGN( tri->numVerts * sizeof( idDrawVert ), VERTEX_CACHE_ALIGN ) );
			}
			
			// add the surface for drawing
			// we can re-use some of the values for light interaction surfaces
			baseDrawSurf = ( drawSurf_t* )R_FrameAlloc( sizeof( *baseDrawSurf ), FRAME_ALLOC_DRAW_SURFACE );
			baseDrawSurf->frontEndGeo = tri;
			baseDrawSurf->space = vEntity;
			baseDrawSurf->scissorRect = vEntity->scissorRect;
			baseDrawSurf->extraGLState = 0;
			baseDrawSurf->renderZFail = 0;
			
			R_SetupDrawSurfShader( baseDrawSurf, shader, renderEntity );
			
			// Check for deformations (eyeballs, flares, etc)
			const deform_t shaderDeform = shader->Deform();
			if( shaderDeform != DFRM_NONE )
			{
				drawSurf_t* deformDrawSurf = R_DeformDrawSurf( baseDrawSurf );
				if( deformDrawSurf != NULL )
				{
					// any deforms may have created multiple draw surfaces
					for( drawSurf_t* surf = deformDrawSurf, * next = NULL; surf != NULL; surf = next )
					{
						next = surf->nextOnLight;
						
						surf->linkChain = NULL;
						surf->nextOnLight = vEntity->drawSurfs;
						vEntity->drawSurfs = surf;
					}
				}
			}
			
			// Most deform source surfaces do not need to be rendered.
			// However, particles are rendered in conjunction with the source surface.
			if( shaderDeform == DFRM_NONE || shaderDeform == DFRM_PARTICLE || shaderDeform == DFRM_PARTICLE2 )
			{
				// copy verts and indexes to this frame's hardware memory if they aren't already there
				if( !vertexCache.CacheIsCurrent( tri->ambientCache ) )
				{
					tri->ambientCache = vertexCache.AllocVertex( tri->verts, ALIGN( tri->numVerts * sizeof( tri->verts[0] ), VERTEX_CACHE_ALIGN ) );
				}
				if( !vertexCache.CacheIsCurrent( tri->indexCache ) )
				{
					tri->indexCache = vertexCache.AllocIndex( tri->indexes, ALIGN( tri->numIndexes * sizeof( tri->indexes[0] ), INDEX_CACHE_ALIGN ) );
				}
				
				R_SetupDrawSurfJoints( baseDrawSurf, tri, shader );
				
				baseDrawSurf->numIndexes = tri->numIndexes;
				baseDrawSurf->ambientCache = tri->ambientCache;
				baseDrawSurf->indexCache = tri->indexCache;
				baseDrawSurf->shadowCache = 0;
				
				baseDrawSurf->linkChain = NULL;		// link to the view
				baseDrawSurf->nextOnLight = vEntity->drawSurfs;
				vEntity->drawSurfs = baseDrawSurf;
			}
		}
		
		//----------------------------------------
		// add all light interactions
		//----------------------------------------
		for( int contactedLight = 0; contactedLight < numContactedLights; contactedLight++ )
		{
			viewLight_t* vLight = contactedLights[contactedLight];
			const idRenderLightLocal* lightDef = vLight->lightDef;
			const idInteraction* interaction = staticInteractions[contactedLight];
			
			// check for a static interaction
			surfaceInteraction_t* surfInter = NULL;
			if( interaction > INTERACTION_EMPTY && interaction->staticInteraction )
			{
				// we have a static interaction that was calculated accurately
				assert( model->NumSurfaces() == interaction->numSurfaces );
				surfInter = &interaction->surfaces[surfaceNum];
			}
			else
			{
				// try to do a more precise cull of this model surface to the light
				if( R_CullModelBoundsToLight( lightDef, tri->bounds, entityDef->modelRenderMatrix ) )
				{
					continue;
				}
			}
			
			// "invisible ink" lights and shaders (imp spawn drawing on walls, etc)
			if( shader->Spectrum() != lightDef->lightShader->Spectrum() )
			{
				continue;
			}
			
			// Calculate the local light origin to determine if the view is inside the shadow
			// projection and to calculate the triangle facing for dynamic shadow volumes.
			idVec3 localLightOrigin;
			R_GlobalPointToLocal( vEntity->modelMatrix, lightDef->globalLightOrigin, localLightOrigin );
			
			//--------------------------
			// surface light interactions
			//--------------------------
			
			dynamicShadowVolumeParms_t* dynamicShadowParms = NULL;
			
			if( addInteractions && surfaceDirectlyVisible && shader->ReceivesLighting() )
			{
				// static interactions can commonly find that no triangles from a surface
				// contact the light, even when the total model does
				if( surfInter == NULL || surfInter->lightTrisIndexCache > 0 )
				{
					// create a drawSurf for this interaction
					drawSurf_t* lightDrawSurf = ( drawSurf_t* )R_FrameAlloc( sizeof( *lightDrawSurf ), FRAME_ALLOC_DRAW_SURFACE );
					
					if( surfInter != NULL )
					{
						// optimized static interaction
						lightDrawSurf->numIndexes = surfInter->numLightTrisIndexes;
						lightDrawSurf->indexCache = surfInter->lightTrisIndexCache;
					}
					else
					{
						// throw the entire source surface at it without any per-triangle culling
						lightDrawSurf->numIndexes = tri->numIndexes;
						lightDrawSurf->indexCache = tri->indexCache;
						
						// optionally cull the triangles to the light volume
						if( r_cullDynamicLightTriangles.GetBool() )
						{
						
							vertCacheHandle_t lightIndexCache = vertexCache.AllocIndex( NULL, ALIGN( lightDrawSurf->numIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) );
							if( vertexCache.CacheIsCurrent( lightIndexCache ) )
							{
								lightDrawSurf->indexCache = lightIndexCache;
								
								dynamicShadowParms = ( dynamicShadowVolumeParms_t* )R_FrameAlloc( sizeof( dynamicShadowParms[0] ), FRAME_ALLOC_SHADOW_VOLUME_PARMS );
								
								dynamicShadowParms->verts = tri->verts;
								dynamicShadowParms->numVerts = tri->numVerts;
								dynamicShadowParms->indexes = tri->indexes;
								dynamicShadowParms->numIndexes = tri->numIndexes;
								dynamicShadowParms->silEdges = tri->silEdges;
								dynamicShadowParms->numSilEdges = tri->numSilEdges;
								dynamicShadowParms->joints = gpuSkinned ? tri->staticModelWithJoints->jointsInverted : NULL;
								dynamicShadowParms->numJoints = gpuSkinned ? tri->staticModelWithJoints->numInvertedJoints : 0;
								dynamicShadowParms->triangleBounds = tri->bounds;
								dynamicShadowParms->triangleMVP = vEntity->mvp;
								dynamicShadowParms->localLightOrigin = localLightOrigin;
								dynamicShadowParms->localViewOrigin = localViewOrigin;
								idRenderMatrix::Multiply( vLight->lightDef->baseLightProject, entityDef->modelRenderMatrix, dynamicShadowParms->localLightProject );
								dynamicShadowParms->zNear = znear;
								dynamicShadowParms->lightZMin = vLight->scissorRect.zmin;
								dynamicShadowParms->lightZMax = vLight->scissorRect.zmax;
								dynamicShadowParms->cullShadowTrianglesToLight = false;
								dynamicShadowParms->forceShadowCaps = false;
								dynamicShadowParms->useShadowPreciseInsideTest = false;
								dynamicShadowParms->useShadowDepthBounds = false;
								dynamicShadowParms->tempFacing = NULL;
								dynamicShadowParms->tempCulled = NULL;
								dynamicShadowParms->tempVerts = NULL;
								dynamicShadowParms->indexBuffer = NULL;
								dynamicShadowParms->shadowIndices = NULL;
								dynamicShadowParms->maxShadowIndices = 0;
								dynamicShadowParms->numShadowIndices = NULL;
								dynamicShadowParms->lightIndices = ( triIndex_t* )vertexCache.MappedIndexBuffer( lightIndexCache );
								dynamicShadowParms->maxLightIndices = lightDrawSurf->numIndexes;
								dynamicShadowParms->numLightIndices = &lightDrawSurf->numIndexes;
								dynamicShadowParms->renderZFail = NULL;
								dynamicShadowParms->shadowZMin = NULL;
								dynamicShadowParms->shadowZMax = NULL;
								dynamicShadowParms->shadowVolumeState = & lightDrawSurf->shadowVolumeState;
								
								lightDrawSurf->shadowVolumeState = SHADOWVOLUME_UNFINISHED;
								
								dynamicShadowParms->next = vEntity->dynamicShadowVolumes;
								vEntity->dynamicShadowVolumes = dynamicShadowParms;
							}
						}
					}
					lightDrawSurf->ambientCache = tri->ambientCache;
					lightDrawSurf->shadowCache = 0;
					lightDrawSurf->frontEndGeo = tri;
					lightDrawSurf->space = vEntity;
					lightDrawSurf->material = shader;
					lightDrawSurf->extraGLState = 0;
					lightDrawSurf->scissorRect = vLight->scissorRect; // interactionScissor;
					lightDrawSurf->sort = 0.0f;
					lightDrawSurf->renderZFail = 0;
					lightDrawSurf->shaderRegisters = baseDrawSurf->shaderRegisters;
					
					R_SetupDrawSurfJoints( lightDrawSurf, tri, shader );
					
					// Determine which linked list to add the light surface to.
					// There will only be localSurfaces if the light casts shadows and
					// there are surfaces with NOSELFSHADOW.
					if( shader->Coverage() == MC_TRANSLUCENT )
					{
						lightDrawSurf->linkChain = &vLight->translucentInteractions;
					}
					else if( !lightDef->parms.noShadows && shader->TestMaterialFlag( MF_NOSELFSHADOW ) )
					{
						lightDrawSurf->linkChain = &vLight->localInteractions;
					}
					else
					{
						lightDrawSurf->linkChain = &vLight->globalInteractions;
					}
					lightDrawSurf->nextOnLight = vEntity->drawSurfs;
					vEntity->drawSurfs = lightDrawSurf;
				}
			}
			
			//--------------------------
			// surface shadows
			//--------------------------
			
			if( !shader->SurfaceCastsShadow() )
			{
				continue;
			}
			if( !lightDef->LightCastsShadows() )
			{
				continue;
			}
			if( tri->silEdges == NULL )
			{
				continue;		// can happen for beam models (shouldn't use a shadow casting material, though...)
			}
			
			// if the static shadow does not have any shadows
			if( surfInter != NULL && surfInter->numShadowIndexes == 0 )
			{
				continue;
			}
			
			// some entities, like view weapons, don't cast any shadows
			if( entityDef->parms.noShadow )
			{
				continue;
			}
			
			// No shadow if it's suppressed for this light.
			if( entityDef->parms.suppressShadowInLightID && entityDef->parms.suppressShadowInLightID == lightDef->parms.lightId )
			{
				continue;
			}
			
			if( lightDef->parms.prelightModel && lightDef->lightHasMoved == false &&
					entityDef->parms.hModel->IsStaticWorldModel() && !r_skipPrelightShadows.GetBool() )
			{
				// static light / world model shadow interacitons
				// are always captured in the prelight shadow volume
				continue;
			}
			
			// If the shadow is drawn (or translucent), but the model isn't, we must include the shadow caps
			// because we may be able to see into the shadow volume even though the view is outside it.
			// This happens for the player world weapon and possibly some animations in multiplayer.
			const bool forceShadowCaps = !addInteractions || r_forceShadowCaps.GetBool();
			
			drawSurf_t* shadowDrawSurf = ( drawSurf_t* )R_FrameAlloc( sizeof( *shadowDrawSurf ), FRAME_ALLOC_DRAW_SURFACE );
			
			if( surfInter != NULL )
			{
				shadowDrawSurf->numIndexes = 0;
				shadowDrawSurf->indexCache = surfInter->shadowIndexCache;
				shadowDrawSurf->shadowCache = tri->shadowCache;
				shadowDrawSurf->scissorRect = vLight->scissorRect;		// default to the light scissor and light depth bounds
				shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_DONE;	// assume the shadow volume is done in case r_skipStaticShadows is set
				
				if( !r_skipStaticShadows.GetBool() )
				{
					staticShadowVolumeParms_t* staticShadowParms = ( staticShadowVolumeParms_t* )R_FrameAlloc( sizeof( staticShadowParms[0] ), FRAME_ALLOC_SHADOW_VOLUME_PARMS );
					
					staticShadowParms->verts = tri->staticShadowVertexes;
					staticShadowParms->numVerts = tri->numVerts * 2;
					staticShadowParms->indexes = surfInter->shadowIndexes;
					staticShadowParms->numIndexes = surfInter->numShadowIndexes;
					staticShadowParms->numShadowIndicesWithCaps = surfInter->numShadowIndexes;
					staticShadowParms->numShadowIndicesNoCaps = surfInter->numShadowIndexesNoCaps;
					staticShadowParms->triangleBounds = tri->bounds;
					staticShadowParms->triangleMVP = vEntity->mvp;
					staticShadowParms->localLightOrigin = localLightOrigin;
					staticShadowParms->localViewOrigin = localViewOrigin;
					staticShadowParms->zNear = znear;
					staticShadowParms->lightZMin = vLight->scissorRect.zmin;
					staticShadowParms->lightZMax = vLight->scissorRect.zmax;
					staticShadowParms->forceShadowCaps = forceShadowCaps;
					staticShadowParms->useShadowPreciseInsideTest = r_useShadowPreciseInsideTest.GetBool();
					staticShadowParms->useShadowDepthBounds = r_useShadowDepthBounds.GetBool();
					staticShadowParms->numShadowIndices = & shadowDrawSurf->numIndexes;
					staticShadowParms->renderZFail = & shadowDrawSurf->renderZFail;
					staticShadowParms->shadowZMin = & shadowDrawSurf->scissorRect.zmin;
					staticShadowParms->shadowZMax = & shadowDrawSurf->scissorRect.zmax;
					staticShadowParms->shadowVolumeState = & shadowDrawSurf->shadowVolumeState;
					
					shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_UNFINISHED;
					
					staticShadowParms->next = vEntity->staticShadowVolumes;
					vEntity->staticShadowVolumes = staticShadowParms;
				}
				
			}
			else
			{
				// When CPU skinning the dynamic shadow verts of a dynamic model may not have been copied to buffer memory yet.
				if( !vertexCache.CacheIsCurrent( tri->shadowCache ) )
				{
					assert( !gpuSkinned );	// the shadow cache should be static when using GPU skinning
					// Extracts just the xyz values from a set of full size drawverts, and
					// duplicates them with w set to 0 and 1 for the vertex program to project.
					// This is constant for any number of lights, the vertex program takes care
					// of projecting the verts to infinity for a particular light.
					tri->shadowCache = vertexCache.AllocVertex( NULL, ALIGN( tri->numVerts * 2 * sizeof( idShadowVert ), VERTEX_CACHE_ALIGN ) );
					idShadowVert* shadowVerts = ( idShadowVert* )vertexCache.MappedVertexBuffer( tri->shadowCache );
					idShadowVert::CreateShadowCache( shadowVerts, tri->verts, tri->numVerts );
				}
				
				const int maxShadowVolumeIndexes = tri->numSilEdges * 6 + tri->numIndexes * 2;
				
				shadowDrawSurf->numIndexes = 0;
				shadowDrawSurf->indexCache = vertexCache.AllocIndex( NULL, ALIGN( maxShadowVolumeIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) );
				shadowDrawSurf->shadowCache = tri->shadowCache;
				shadowDrawSurf->scissorRect = vLight->scissorRect;		// default to the light scissor and light depth bounds
				shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_DONE;	// assume the shadow volume is done in case the index cache allocation failed
				
				// if the index cache was successfully allocated then setup the parms to create a shadow volume in parallel
				if( vertexCache.CacheIsCurrent( shadowDrawSurf->indexCache ) && !r_skipDynamicShadows.GetBool() )
				{
				
					// if the parms were not already allocated for culling interaction triangles to the light frustum
					if( dynamicShadowParms == NULL )
					{
						dynamicShadowParms = ( dynamicShadowVolumeParms_t* )R_FrameAlloc( sizeof( dynamicShadowParms[0] ), FRAME_ALLOC_SHADOW_VOLUME_PARMS );
					}
					else
					{
						// the shadow volume will be rendered first so when the interaction surface is drawn the triangles have been culled for sure
						*dynamicShadowParms->shadowVolumeState = SHADOWVOLUME_DONE;
					}
					
					dynamicShadowParms->verts = tri->verts;
					dynamicShadowParms->numVerts = tri->numVerts;
					dynamicShadowParms->indexes = tri->indexes;
					dynamicShadowParms->numIndexes = tri->numIndexes;
					dynamicShadowParms->silEdges = tri->silEdges;
					dynamicShadowParms->numSilEdges = tri->numSilEdges;
					dynamicShadowParms->joints = gpuSkinned ? tri->staticModelWithJoints->jointsInverted : NULL;
					dynamicShadowParms->numJoints = gpuSkinned ? tri->staticModelWithJoints->numInvertedJoints : 0;
					dynamicShadowParms->triangleBounds = tri->bounds;
					dynamicShadowParms->triangleMVP = vEntity->mvp;
					dynamicShadowParms->localLightOrigin = localLightOrigin;
					dynamicShadowParms->localViewOrigin = localViewOrigin;
					idRenderMatrix::Multiply( vLight->lightDef->baseLightProject, entityDef->modelRenderMatrix, dynamicShadowParms->localLightProject );
					dynamicShadowParms->zNear = znear;
					dynamicShadowParms->lightZMin = vLight->scissorRect.zmin;
					dynamicShadowParms->lightZMax = vLight->scissorRect.zmax;
					dynamicShadowParms->cullShadowTrianglesToLight = r_cullDynamicShadowTriangles.GetBool();
					dynamicShadowParms->forceShadowCaps = forceShadowCaps;
					dynamicShadowParms->useShadowPreciseInsideTest = r_useShadowPreciseInsideTest.GetBool();
					dynamicShadowParms->useShadowDepthBounds = r_useShadowDepthBounds.GetBool();
					dynamicShadowParms->tempFacing = NULL;
					dynamicShadowParms->tempCulled = NULL;
					dynamicShadowParms->tempVerts = NULL;
					dynamicShadowParms->indexBuffer = NULL;
					dynamicShadowParms->shadowIndices = ( triIndex_t* )vertexCache.MappedIndexBuffer( shadowDrawSurf->indexCache );
					dynamicShadowParms->maxShadowIndices = maxShadowVolumeIndexes;
					dynamicShadowParms->numShadowIndices = & shadowDrawSurf->numIndexes;
					// dynamicShadowParms->lightIndices may have already been set for the interaction surface
					// dynamicShadowParms->maxLightIndices may have already been set for the interaction surface
					// dynamicShadowParms->numLightIndices may have already been set for the interaction surface
					dynamicShadowParms->renderZFail = & shadowDrawSurf->renderZFail;
					dynamicShadowParms->shadowZMin = & shadowDrawSurf->scissorRect.zmin;
					dynamicShadowParms->shadowZMax = & shadowDrawSurf->scissorRect.zmax;
					dynamicShadowParms->shadowVolumeState = & shadowDrawSurf->shadowVolumeState;
					
					shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_UNFINISHED;
					
					// if the parms we not already linked for culling interaction triangles to the light frustum
					if( dynamicShadowParms->lightIndices == NULL )
					{
						dynamicShadowParms->next = vEntity->dynamicShadowVolumes;
						vEntity->dynamicShadowVolumes = dynamicShadowParms;
					}
					
					tr.pc.c_createShadowVolumes++;
				}
			}
			
			assert( vertexCache.CacheIsCurrent( shadowDrawSurf->shadowCache ) );
			assert( vertexCache.CacheIsCurrent( shadowDrawSurf->indexCache ) );
			
			shadowDrawSurf->ambientCache = 0;
			shadowDrawSurf->frontEndGeo = NULL;
			shadowDrawSurf->space = vEntity;
			shadowDrawSurf->material = NULL;
			shadowDrawSurf->extraGLState = 0;
			shadowDrawSurf->sort = 0.0f;
			shadowDrawSurf->shaderRegisters = NULL;
			
			R_SetupDrawSurfJoints( shadowDrawSurf, tri, NULL );
			
			// determine which linked list to add the shadow surface to
			shadowDrawSurf->linkChain = shader->TestMaterialFlag( MF_NOSELFSHADOW ) ? &vLight->localShadows : &vLight->globalShadows;
			shadowDrawSurf->nextOnLight = vEntity->drawSurfs;
			vEntity->drawSurfs = shadowDrawSurf;
		}
	}
}
示例#15
0
/*
================
EmitSurfaces

For full screen GUIs, we can add in per-surface stereoscopic depth effects
================
*/
void idGuiModel::EmitSurfaces( float modelMatrix[16], float modelViewMatrix[16],
							   bool depthHack, bool allowFullScreenStereoDepth, bool linkAsEntity )
{

	viewEntity_t* guiSpace = ( viewEntity_t* )R_ClearedFrameAlloc( sizeof( *guiSpace ), FRAME_ALLOC_VIEW_ENTITY );
	memcpy( guiSpace->modelMatrix, modelMatrix, sizeof( guiSpace->modelMatrix ) );
	memcpy( guiSpace->modelViewMatrix, modelViewMatrix, sizeof( guiSpace->modelViewMatrix ) );
	guiSpace->weaponDepthHack = depthHack;
	guiSpace->isGuiSurface = true;
	
	// If this is an in-game gui, we need to be able to find the matrix again for head mounted
	// display bypass matrix fixup.
	if( linkAsEntity )
	{
		guiSpace->next = tr.viewDef->viewEntitys;
		tr.viewDef->viewEntitys = guiSpace;
	}
	
	//---------------------------
	// make a tech5 renderMatrix
	//---------------------------
	idRenderMatrix viewMat;
	idRenderMatrix::Transpose( *( idRenderMatrix* )modelViewMatrix, viewMat );
	idRenderMatrix::Multiply( tr.viewDef->projectionRenderMatrix, viewMat, guiSpace->mvp );
	if( depthHack )
	{
		idRenderMatrix::ApplyDepthHack( guiSpace->mvp );
	}
	
	// to allow 3D-TV effects in the menu system, we define surface flags to set
	// depth fractions between 0=screen and 1=infinity, which directly modulate the
	// screenSeparation parameter for an X offset.
	// The value is stored in the drawSurf sort value, which adjusts the matrix in the
	// backend.
	float defaultStereoDepth = stereoRender_defaultGuiDepth.GetFloat();	// default to at-screen
	
	// add the surfaces to this view
	for( int i = 0; i < surfaces.Num(); i++ )
	{
		const guiModelSurface_t& guiSurf = surfaces[i];
		if( guiSurf.numIndexes == 0 )
		{
			continue;
		}
		
		const idMaterial* shader = guiSurf.material;
		drawSurf_t* drawSurf = ( drawSurf_t* )R_FrameAlloc( sizeof( *drawSurf ), FRAME_ALLOC_DRAW_SURFACE );
		
		drawSurf->numIndexes = guiSurf.numIndexes;
		drawSurf->ambientCache = vertexBlock;
		// build a vertCacheHandle_t that points inside the allocated block
		drawSurf->indexCache = indexBlock + ( ( int64 )( guiSurf.firstIndex * sizeof( triIndex_t ) ) << VERTCACHE_OFFSET_SHIFT );
		drawSurf->shadowCache = 0;
		drawSurf->jointCache = 0;
		drawSurf->frontEndGeo = NULL;
		drawSurf->space = guiSpace;
		drawSurf->material = shader;
		drawSurf->extraGLState = guiSurf.glState;
		drawSurf->scissorRect = tr.viewDef->scissor;
		drawSurf->sort = shader->GetSort();
		drawSurf->renderZFail = 0;
		// 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 ), FRAME_ALLOC_SHADER_REGISTER );
			drawSurf->shaderRegisters = regs;
			shader->EvaluateRegisters( regs, shaderParms, tr.viewDef->renderView.shaderParms, tr.viewDef->renderView.time[1] * 0.001f, NULL );
		}
		R_LinkDrawSurfToView( drawSurf, tr.viewDef );
		if( allowFullScreenStereoDepth )
		{
			// override sort with the stereoDepth
			//drawSurf->sort = stereoDepth;
			
			switch( guiSurf.stereoType )
			{
				case STEREO_DEPTH_TYPE_NEAR:
					drawSurf->sort = STEREO_DEPTH_NEAR;
					break;
				case STEREO_DEPTH_TYPE_MID:
					drawSurf->sort = STEREO_DEPTH_MID;
					break;
				case STEREO_DEPTH_TYPE_FAR:
					drawSurf->sort = STEREO_DEPTH_FAR;
					break;
				case STEREO_DEPTH_TYPE_NONE:
				default:
					drawSurf->sort = defaultStereoDepth;
					break;
			}
		}
	}
}
示例#16
0
/*
================
idGuiModel::EmitFullScreen

Creates a view that covers the screen and emit the surfaces
================
*/
void idGuiModel::EmitFullScreen()
{

	if( surfaces[0].numIndexes == 0 )
	{
		return;
	}
	
	SCOPED_PROFILE_EVENT( "Gui::EmitFullScreen" );
	
	viewDef_t* viewDef = ( viewDef_t* )R_ClearedFrameAlloc( sizeof( *viewDef ), FRAME_ALLOC_VIEW_DEF );
	viewDef->is2Dgui = true;
	tr.GetCroppedViewport( &viewDef->viewport );
	
	bool stereoEnabled = ( renderSystem->GetStereo3DMode() != STEREO3D_OFF );
	if( stereoEnabled )
	{
		const float screenSeparation = GetScreenSeparationForGuis();
		
		// this will be negated on the alternate eyes, both rendered each frame
		viewDef->renderView.stereoScreenSeparation = screenSeparation;
		
		extern idCVar stereoRender_swapEyes;
		viewDef->renderView.viewEyeBuffer = 0;	// render to both buffers
		if( stereoRender_swapEyes.GetBool() )
		{
			viewDef->renderView.stereoScreenSeparation = -screenSeparation;
		}
	}
	
	viewDef->scissor.x1 = 0;
	viewDef->scissor.y1 = 0;
	viewDef->scissor.x2 = viewDef->viewport.x2 - viewDef->viewport.x1;
	viewDef->scissor.y2 = viewDef->viewport.y2 - viewDef->viewport.y1;
	
	viewDef->projectionMatrix[0 * 4 + 0] = 2.0f / renderSystem->GetVirtualWidth();
	viewDef->projectionMatrix[0 * 4 + 1] = 0.0f;
	viewDef->projectionMatrix[0 * 4 + 2] = 0.0f;
	viewDef->projectionMatrix[0 * 4 + 3] = 0.0f;
	
	viewDef->projectionMatrix[1 * 4 + 0] = 0.0f;
	viewDef->projectionMatrix[1 * 4 + 1] = -2.0f / renderSystem->GetVirtualHeight();
	viewDef->projectionMatrix[1 * 4 + 2] = 0.0f;
	viewDef->projectionMatrix[1 * 4 + 3] = 0.0f;
	
	viewDef->projectionMatrix[2 * 4 + 0] = 0.0f;
	viewDef->projectionMatrix[2 * 4 + 1] = 0.0f;
	viewDef->projectionMatrix[2 * 4 + 2] = -2.0f;
	viewDef->projectionMatrix[2 * 4 + 3] = 0.0f;
	
	viewDef->projectionMatrix[3 * 4 + 0] = -1.0f;
	viewDef->projectionMatrix[3 * 4 + 1] = 1.0f;
	viewDef->projectionMatrix[3 * 4 + 2] = -1.0f;
	viewDef->projectionMatrix[3 * 4 + 3] = 1.0f;
	
	// make a tech5 renderMatrix for faster culling
	idRenderMatrix::Transpose( *( idRenderMatrix* )viewDef->projectionMatrix, viewDef->projectionRenderMatrix );
	
	viewDef->worldSpace.modelMatrix[0 * 4 + 0] = 1.0f;
	viewDef->worldSpace.modelMatrix[1 * 4 + 1] = 1.0f;
	viewDef->worldSpace.modelMatrix[2 * 4 + 2] = 1.0f;
	viewDef->worldSpace.modelMatrix[3 * 4 + 3] = 1.0f;
	
	viewDef->worldSpace.modelViewMatrix[0 * 4 + 0] = 1.0f;
	viewDef->worldSpace.modelViewMatrix[1 * 4 + 1] = 1.0f;
	viewDef->worldSpace.modelViewMatrix[2 * 4 + 2] = 1.0f;
	viewDef->worldSpace.modelViewMatrix[3 * 4 + 3] = 1.0f;
	
	viewDef->maxDrawSurfs = surfaces.Num();
	viewDef->drawSurfs = ( drawSurf_t** )R_FrameAlloc( viewDef->maxDrawSurfs * sizeof( viewDef->drawSurfs[0] ), FRAME_ALLOC_DRAW_SURFACE_POINTER );
	viewDef->numDrawSurfs = 0;
	
#if 1
	// RB: give renderView the current time to calculate 2D shader effects
	int shaderTime = tr.frameShaderTime * 1000; //Sys_Milliseconds();
	viewDef->renderView.time[0] = shaderTime;
	viewDef->renderView.time[1] = shaderTime;
	// RB end
#endif
	
	viewDef_t* oldViewDef = tr.viewDef;
	tr.viewDef = viewDef;
	
	EmitSurfaces( viewDef->worldSpace.modelMatrix, viewDef->worldSpace.modelViewMatrix,
				  false /* depthHack */ , stereoEnabled /* stereoDepthSort */, false /* link as entity */ );
				  
	tr.viewDef = oldViewDef;
	
	// add the command to draw this view
	R_AddDrawViewCmd( viewDef, true );
}
示例#17
0
/*
=================
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;
		default:
			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 ) )
		{
			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
}
示例#18
0
/*
==================
R_ClearedFrameAlloc
==================
*/
void *R_ClearedFrameAlloc( int bytes, frameAllocType_t type ) {
	// NOTE: every allocation is cache line cleared
	return R_FrameAlloc( bytes, type );
}
示例#19
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 );
		}
	}
}