DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

RenderWorld_portals.cpp (30705B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #pragma hdrstop
     30 #include "../idlib/precompiled.h"
     31 
     32 #include "tr_local.h"
     33 
     34 // if we hit this many planes, we will just stop cropping the
     35 // view down, which is still correct, just conservative
     36 const int MAX_PORTAL_PLANES	= 20;
     37 
     38 struct portalStack_t {
     39 	const portal_t *		p;
     40 	const portalStack_t *	next;
     41 	// positive side is outside the visible frustum
     42 	int						numPortalPlanes;
     43 	idPlane					portalPlanes[MAX_PORTAL_PLANES+1];
     44 	idScreenRect			rect;
     45 };
     46 
     47 /*
     48 =======================================================================
     49 
     50 Create viewLights and viewEntitys for the lights and entities that are
     51 visible in the portal areas that can be seen from the current viewpoint.
     52 
     53 =======================================================================
     54 */
     55 
     56 /*
     57 =============
     58 R_SetLightDefViewLight
     59 
     60 If the lightDef is not already on the viewLight list, create
     61 a viewLight and add it to the list with an empty scissor rect.
     62 =============
     63 */
     64 viewLight_t *R_SetLightDefViewLight( idRenderLightLocal *light ) {
     65 	if ( light->viewCount == tr.viewCount ) {
     66 		// already set up for this frame
     67 		return light->viewLight;
     68 	}
     69 	light->viewCount = tr.viewCount;
     70 
     71 	// add to the view light chain
     72 	viewLight_t * vLight = (viewLight_t *)R_ClearedFrameAlloc( sizeof( *vLight ), FRAME_ALLOC_VIEW_LIGHT );
     73 	vLight->lightDef = light;
     74 
     75 	// the scissorRect will be expanded as the light bounds is accepted into visible portal chains
     76 	// and the scissor will be reduced in R_AddSingleLight based on the screen space projection
     77 	vLight->scissorRect.Clear();
     78 
     79 	// link the view light
     80 	vLight->next = tr.viewDef->viewLights;
     81 	tr.viewDef->viewLights = vLight;
     82 
     83 	light->viewLight = vLight;
     84 
     85 	return vLight;
     86 }
     87 
     88 /*
     89 =============
     90 R_SetEntityDefViewEntity
     91 
     92 If the entityDef is not already on the viewEntity list, create
     93 a viewEntity and add it to the list with an empty scissor rect.
     94 =============
     95 */
     96 viewEntity_t *R_SetEntityDefViewEntity( idRenderEntityLocal *def ) {
     97 	if ( def->viewCount == tr.viewCount ) {
     98 		// already set up for this frame
     99 		return def->viewEntity;
    100 	}
    101 	def->viewCount = tr.viewCount;
    102 
    103 	viewEntity_t * vModel = (viewEntity_t *)R_ClearedFrameAlloc( sizeof( *vModel ), FRAME_ALLOC_VIEW_ENTITY );
    104 	vModel->entityDef = def;
    105 
    106 	// the scissorRect will be expanded as the model bounds is accepted into visible portal chains
    107 	// It will remain clear if the model is only needed for shadows.
    108 	vModel->scissorRect.Clear();
    109 
    110 	vModel->next = tr.viewDef->viewEntitys;
    111 	tr.viewDef->viewEntitys = vModel;
    112 
    113 	def->viewEntity = vModel;
    114 
    115 	return vModel;
    116 }
    117 
    118 /*
    119 ================
    120 CullEntityByPortals
    121 
    122 Return true if the entity reference bounds do not intersect the current portal chain.
    123 ================
    124 */
    125 bool idRenderWorldLocal::CullEntityByPortals( const idRenderEntityLocal *entity, const portalStack_t *ps ) {
    126 	if ( r_useEntityPortalCulling.GetInteger() == 1 ) {
    127 
    128 		ALIGNTYPE16 frustumCorners_t corners;
    129 		idRenderMatrix::GetFrustumCorners( corners, entity->inverseBaseModelProject, bounds_unitCube );
    130 		for ( int i = 0; i < ps->numPortalPlanes; i++ ) {
    131 			if ( idRenderMatrix::CullFrustumCornersToPlane( corners, ps->portalPlanes[i] ) == FRUSTUM_CULL_FRONT ) {
    132 				return true;
    133 			}
    134 		}
    135 
    136 	} else if ( r_useEntityPortalCulling.GetInteger() >= 2 ) {
    137 
    138 		idRenderMatrix baseModelProject;
    139 		idRenderMatrix::Inverse( entity->inverseBaseModelProject, baseModelProject );
    140 
    141 		idPlane frustumPlanes[6];
    142 		idRenderMatrix::GetFrustumPlanes( frustumPlanes, baseModelProject, false, true );
    143 
    144 		// exact clip of light faces against all planes
    145 		for ( int i = 0; i < 6; i++ ) {
    146 			// the entity frustum planes face inward, so the planes that have the
    147 			// view origin on the positive side will be the "back" faces of the entity,
    148 			// which must have some fragment inside the portal stack planes to be visible
    149 			if ( frustumPlanes[i].Distance( tr.viewDef->renderView.vieworg ) <= 0.0f ) {
    150 				continue;
    151 			}
    152 
    153 			// calculate a winding for this frustum side
    154 			idFixedWinding w;
    155 			w.BaseForPlane( frustumPlanes[i] );
    156 			for ( int j = 0; j < 6; j++ ) {
    157 				if ( j == i ) {
    158 					continue;
    159 				}
    160 				if ( !w.ClipInPlace( frustumPlanes[j], ON_EPSILON ) ) {
    161 					break;
    162 				}
    163 			}
    164 			if ( w.GetNumPoints() <= 2 ) {
    165 				continue;
    166 			}
    167 
    168 			assert( ps->numPortalPlanes <= MAX_PORTAL_PLANES );
    169 			assert( w.GetNumPoints() + ps->numPortalPlanes < MAX_POINTS_ON_WINDING );
    170 
    171 			// now clip the winding against each of the portalStack planes
    172 			// skip the last plane which is the last portal itself
    173 			for ( int j = 0; j < ps->numPortalPlanes - 1; j++ ) {
    174 				if ( !w.ClipInPlace( -ps->portalPlanes[j], ON_EPSILON ) ) {
    175 					break;
    176 				}
    177 			}
    178 
    179 			if ( w.GetNumPoints() > 2 ) {
    180 				// part of the winding is visible through the portalStack,
    181 				// so the entity is not culled
    182 				return false;
    183 			}
    184 		}
    185 
    186 		// nothing was visible
    187 		return true;
    188 
    189 	}
    190 
    191 	return false;
    192 }
    193 
    194 /*
    195 ===================
    196 AddAreaViewEntities
    197 
    198 Any models that are visible through the current portalStack will have their scissor rect updated.
    199 ===================
    200 */
    201 void idRenderWorldLocal::AddAreaViewEntities( int areaNum, const portalStack_t *ps ) {
    202 	portalArea_t * area = &portalAreas[ areaNum ];
    203 
    204 	for ( areaReference_t * ref = area->entityRefs.areaNext; ref != &area->entityRefs; ref = ref->areaNext ) {
    205 		idRenderEntityLocal	* entity = ref->entity;
    206 
    207 		// debug tool to allow viewing of only one entity at a time
    208 		if ( r_singleEntity.GetInteger() >= 0 && r_singleEntity.GetInteger() != entity->index ) {
    209 			continue;
    210 		}
    211 
    212 		// remove decals that are completely faded away
    213 		R_FreeEntityDefFadedDecals( entity, tr.viewDef->renderView.time[0] );
    214 
    215 		// check for completely suppressing the model
    216 		if ( !r_skipSuppress.GetBool() ) {
    217 			if ( entity->parms.suppressSurfaceInViewID
    218 					&& entity->parms.suppressSurfaceInViewID == tr.viewDef->renderView.viewID ) {
    219 				continue;
    220 			}
    221 			if ( entity->parms.allowSurfaceInViewID 
    222 					&& entity->parms.allowSurfaceInViewID != tr.viewDef->renderView.viewID ) {
    223 				continue;
    224 			}
    225 		}
    226 
    227 		// cull reference bounds
    228 		if ( CullEntityByPortals( entity, ps ) ) {
    229 			// we are culled out through this portal chain, but it might
    230 			// still be visible through others
    231 			continue;
    232 		}
    233 
    234 		viewEntity_t * vEnt = R_SetEntityDefViewEntity( entity );
    235 
    236 		// possibly expand the scissor rect
    237 		vEnt->scissorRect.Union( ps->rect );
    238 	}
    239 }
    240 
    241 /*
    242 ================
    243 CullLightByPortals
    244 
    245 Return true if the light frustum does not intersect the current portal chain.
    246 ================
    247 */
    248 bool idRenderWorldLocal::CullLightByPortals( const idRenderLightLocal *light, const portalStack_t *ps ) {
    249 	if ( r_useLightPortalCulling.GetInteger() == 1 ) {
    250 
    251 		ALIGNTYPE16 frustumCorners_t corners;
    252 		idRenderMatrix::GetFrustumCorners( corners, light->inverseBaseLightProject, bounds_zeroOneCube );
    253 		for ( int i = 0; i < ps->numPortalPlanes; i++ ) {
    254 			if ( idRenderMatrix::CullFrustumCornersToPlane( corners, ps->portalPlanes[i] ) == FRUSTUM_CULL_FRONT ) {
    255 				return true;
    256 			}
    257 		}
    258 
    259 	} else if ( r_useLightPortalCulling.GetInteger() >= 2 ) {
    260 
    261 		idPlane frustumPlanes[6];
    262 		idRenderMatrix::GetFrustumPlanes( frustumPlanes, light->baseLightProject, true, true );
    263 
    264 		// exact clip of light faces against all planes
    265 		for ( int i = 0; i < 6; i++ ) {
    266 			// the light frustum planes face inward, so the planes that have the
    267 			// view origin on the positive side will be the "back" faces of the light,
    268 			// which must have some fragment inside the the portal stack planes to be visible
    269 			if ( frustumPlanes[i].Distance( tr.viewDef->renderView.vieworg ) <= 0.0f ) {
    270 				continue;
    271 			}
    272 
    273 			// calculate a winding for this frustum side
    274 			idFixedWinding w;
    275 			w.BaseForPlane( frustumPlanes[i] );
    276 			for ( int j = 0; j < 6; j++ ) {
    277 				if ( j == i ) {
    278 					continue;
    279 				}
    280 				if ( !w.ClipInPlace( frustumPlanes[j], ON_EPSILON ) ) {
    281 					break;
    282 				}
    283 			}
    284 			if ( w.GetNumPoints() <= 2 ) {
    285 				continue;
    286 			}
    287 
    288 			assert( ps->numPortalPlanes <= MAX_PORTAL_PLANES );
    289 			assert( w.GetNumPoints() + ps->numPortalPlanes < MAX_POINTS_ON_WINDING );
    290 
    291 			// now clip the winding against each of the portalStack planes
    292 			// skip the last plane which is the last portal itself
    293 			for ( int j = 0; j < ps->numPortalPlanes - 1; j++ ) {
    294 				if ( !w.ClipInPlace( -ps->portalPlanes[j], ON_EPSILON ) ) {
    295 					break;
    296 				}
    297 			}
    298 
    299 			if ( w.GetNumPoints() > 2 ) {
    300 				// part of the winding is visible through the portalStack,
    301 				// so the light is not culled
    302 				return false;
    303 			}
    304 		}
    305 
    306 		// nothing was visible
    307 		return true;
    308 	}
    309 
    310 	return false;
    311 }
    312 
    313 /*
    314 ===================
    315 AddAreaViewLights
    316 
    317 This is the only point where lights get added to the viewLights list.
    318 Any lights that are visible through the current portalStack will have their scissor rect updated.
    319 ===================
    320 */
    321 void idRenderWorldLocal::AddAreaViewLights( int areaNum, const portalStack_t *ps ) {
    322 	portalArea_t * area = &portalAreas[ areaNum ];
    323 
    324 	for ( areaReference_t * lref = area->lightRefs.areaNext; lref != &area->lightRefs; lref = lref->areaNext ) {
    325 		idRenderLightLocal * light = lref->light;
    326 
    327 		// debug tool to allow viewing of only one light at a time
    328 		if ( r_singleLight.GetInteger() >= 0 && r_singleLight.GetInteger() != light->index ) {
    329 			continue;
    330 		}
    331 
    332 		// check for being closed off behind a door
    333 		// a light that doesn't cast shadows will still light even if it is behind a door
    334 		if ( r_useLightAreaCulling.GetBool() && !light->LightCastsShadows()
    335 					&& light->areaNum != -1 && !tr.viewDef->connectedAreas[ light->areaNum ] ) {
    336 			continue;
    337 		}
    338 
    339 		// cull frustum
    340 		if ( CullLightByPortals( light, ps ) ) {
    341 			// we are culled out through this portal chain, but it might
    342 			// still be visible through others
    343 			continue;
    344 		}
    345 
    346 		viewLight_t * vLight = R_SetLightDefViewLight( light );
    347 
    348 		// expand the scissor rect
    349 		vLight->scissorRect.Union( ps->rect );
    350 	}
    351 }
    352 
    353 /*
    354 ===================
    355 AddAreaToView
    356 
    357 This may be entered multiple times with different planes
    358 if more than one portal sees into the area
    359 ===================
    360 */
    361 void idRenderWorldLocal::AddAreaToView( int areaNum, const portalStack_t *ps ) {
    362 	// mark the viewCount, so r_showPortals can display the considered portals
    363 	portalAreas[ areaNum ].viewCount = tr.viewCount;
    364 
    365 	// add the models and lights, using more precise culling to the planes
    366 	AddAreaViewEntities( areaNum, ps );
    367 	AddAreaViewLights( areaNum, ps );
    368 }
    369 
    370 /*
    371 ===================
    372 idRenderWorldLocal::ScreenRectForWinding
    373 ===================
    374 */
    375 idScreenRect idRenderWorldLocal::ScreenRectFromWinding( const idWinding * w, const viewEntity_t * space ) {
    376 	const float viewWidth = (float) tr.viewDef->viewport.x2 - (float) tr.viewDef->viewport.x1;
    377 	const float viewHeight = (float) tr.viewDef->viewport.y2 - (float) tr.viewDef->viewport.y1;
    378 
    379 	idScreenRect r;
    380 	r.Clear();
    381 	for ( int i = 0; i < w->GetNumPoints(); i++ ) {
    382 		idVec3 v;
    383 		idVec3 ndc;
    384 		R_LocalPointToGlobal( space->modelMatrix, (*w)[i].ToVec3(), v );
    385 		R_GlobalToNormalizedDeviceCoordinates( v, ndc );
    386 
    387 		float windowX = ( ndc[0] * 0.5f + 0.5f ) * viewWidth;
    388 		float windowY = ( ndc[1] * 0.5f + 0.5f ) * viewHeight;
    389 
    390 		r.AddPoint( windowX, windowY );
    391 	}
    392 
    393 	r.Expand();
    394 
    395 	return r;
    396 }
    397 
    398 /*
    399 ===================
    400 idRenderWorldLocal::PortalIsFoggedOut
    401 ===================
    402 */
    403 bool idRenderWorldLocal::PortalIsFoggedOut( const portal_t *p ) {
    404 	idRenderLightLocal * ldef = p->doublePortal->fogLight;
    405 	if ( ldef == NULL ) {
    406 		return false;
    407 	}
    408 
    409 	// find the current density of the fog
    410 	const idMaterial * lightShader = ldef->lightShader;
    411 	const int size = lightShader->GetNumRegisters() * sizeof( float );
    412 	float * regs = (float *)_alloca( size );
    413 
    414 	lightShader->EvaluateRegisters( regs, ldef->parms.shaderParms, 
    415 		tr.viewDef->renderView.shaderParms, tr.viewDef->renderView.time[0] * 0.001f, ldef->parms.referenceSound );
    416 
    417 	const shaderStage_t	*stage = lightShader->GetStage(0);
    418 
    419 	const float alpha = regs[ stage->color.registers[3] ];
    420 
    421 	// if they left the default value on, set a fog distance of 500
    422 	float a;
    423 	if ( alpha <= 1.0f ) {
    424 		a = -0.5f / DEFAULT_FOG_DISTANCE;
    425 	} else {
    426 		// otherwise, distance = alpha color
    427 		a = -0.5f / alpha;
    428 	}
    429 
    430 	idPlane forward;
    431 	forward[0] = a * tr.viewDef->worldSpace.modelViewMatrix[0*4+2];
    432 	forward[1] = a * tr.viewDef->worldSpace.modelViewMatrix[1*4+2];
    433 	forward[2] = a * tr.viewDef->worldSpace.modelViewMatrix[2*4+2];
    434 	forward[3] = a * tr.viewDef->worldSpace.modelViewMatrix[3*4+2];
    435 
    436 	const idWinding	* w = p->w;
    437 	for ( int i = 0; i < w->GetNumPoints(); i++ ) {
    438 		const float d = forward.Distance( (*w)[i].ToVec3() );
    439 		if ( d < 0.5f ) {
    440 			return false;		// a point not clipped off
    441 		}
    442 	}
    443 
    444 	return true;
    445 }
    446 
    447 /*
    448 ===================
    449 idRenderWorldLocal::FloodViewThroughArea_r
    450 ===================
    451 */
    452 void idRenderWorldLocal::FloodViewThroughArea_r( const idVec3 & origin, int areaNum, const portalStack_t *ps ) {
    453 	portalArea_t * area = &portalAreas[ areaNum ];
    454 
    455 	// cull models and lights to the current collection of planes
    456 	AddAreaToView( areaNum, ps );
    457 
    458 	if ( areaScreenRect[areaNum].IsEmpty() ) {
    459 		areaScreenRect[areaNum] = ps->rect;
    460 	} else {
    461 		areaScreenRect[areaNum].Union( ps->rect );
    462 	}
    463 
    464 	// go through all the portals
    465 	for ( const portal_t * p = area->portals; p != NULL; p = p->next ) {
    466 		// an enclosing door may have sealed the portal off
    467 		if ( p->doublePortal->blockingBits & PS_BLOCK_VIEW ) {
    468 			continue;
    469 		}
    470 
    471 		// make sure this portal is facing away from the view
    472 		const float d = p->plane.Distance( origin );
    473 		if ( d < -0.1f ) {
    474 			continue;
    475 		}
    476 
    477 		// make sure the portal isn't in our stack trace,
    478 		// which would cause an infinite loop
    479 		const portalStack_t * check = ps;
    480 		for ( ; check != NULL; check = check->next ) {
    481 			if ( check->p == p ) {
    482 				break;		// don't recursively enter a stack
    483 			}
    484 		}
    485 		if ( check ) {
    486 			continue;	// already in stack
    487 		}
    488 
    489 		// if we are very close to the portal surface, don't bother clipping
    490 		// it, which tends to give epsilon problems that make the area vanish
    491 		if ( d < 1.0f ) {
    492 
    493 			// go through this portal
    494 			portalStack_t newStack;
    495 			newStack = *ps;
    496 			newStack.p = p;
    497 			newStack.next = ps;
    498 			FloodViewThroughArea_r( origin, p->intoArea, &newStack );
    499 			continue;
    500 		}
    501 
    502 		// clip the portal winding to all of the planes
    503 		idFixedWinding w;		// we won't overflow because MAX_PORTAL_PLANES = 20
    504 		w = *p->w;
    505 		for ( int j = 0; j < ps->numPortalPlanes; j++ ) {
    506 			if ( !w.ClipInPlace( -ps->portalPlanes[j], 0 ) ) {
    507 				break;
    508 			}
    509 		}
    510 		if ( !w.GetNumPoints() ) {
    511 			continue;	// portal not visible
    512 		}
    513 
    514 		// see if it is fogged out
    515 		if ( PortalIsFoggedOut( p ) ) {
    516 			continue;
    517 		}
    518 
    519 		// go through this portal
    520 		portalStack_t newStack;
    521 		newStack.p = p;
    522 		newStack.next = ps;
    523 
    524 		// find the screen pixel bounding box of the remaining portal
    525 		// so we can scissor things outside it
    526 		newStack.rect = ScreenRectFromWinding( &w, &tr.identitySpace );
    527 		
    528 		// slop might have spread it a pixel outside, so trim it back
    529 		newStack.rect.Intersect( ps->rect );
    530 
    531 		// generate a set of clipping planes that will further restrict
    532 		// the visible view beyond just the scissor rect
    533 
    534 		int addPlanes = w.GetNumPoints();
    535 		if ( addPlanes > MAX_PORTAL_PLANES ) {
    536 			addPlanes = MAX_PORTAL_PLANES;
    537 		}
    538 
    539 		newStack.numPortalPlanes = 0;
    540 		for ( int i = 0; i < addPlanes; i++ ) {
    541 			int j = i + 1;
    542 			if ( j == w.GetNumPoints() ) {
    543 				j = 0;
    544 			}
    545 
    546 			const idVec3 & v1 = origin - w[i].ToVec3();
    547 			const idVec3 & v2 = origin - w[j].ToVec3();
    548 
    549 			newStack.portalPlanes[newStack.numPortalPlanes].Normal().Cross( v2, v1 );
    550 
    551 			// if it is degenerate, skip the plane
    552 			if ( newStack.portalPlanes[newStack.numPortalPlanes].Normalize() < 0.01f ) {
    553 				continue;
    554 			}
    555 			newStack.portalPlanes[newStack.numPortalPlanes].FitThroughPoint( origin );
    556 
    557 			newStack.numPortalPlanes++;
    558 		}
    559 
    560 		// the last stack plane is the portal plane
    561 		newStack.portalPlanes[newStack.numPortalPlanes] = p->plane;
    562 		newStack.numPortalPlanes++;
    563 
    564 		FloodViewThroughArea_r( origin, p->intoArea, &newStack );
    565 	}
    566 }
    567 
    568 /*
    569 =======================
    570 idRenderWorldLocal::FlowViewThroughPortals
    571 
    572 Finds viewLights and viewEntities by flowing from an origin through the visible
    573 portals that the origin point can see into. The planes array defines a volume with
    574 the planes pointing outside the volume. Zero planes assumes an unbounded volume.
    575 =======================
    576 */
    577 void idRenderWorldLocal::FlowViewThroughPortals( const idVec3 & origin, int numPlanes, const idPlane *planes ) {
    578 	portalStack_t ps;
    579 	ps.next = NULL;
    580 	ps.p = NULL;
    581 
    582 	assert( numPlanes <= MAX_PORTAL_PLANES );
    583 	for ( int i = 0; i < numPlanes; i++ ) {
    584 		ps.portalPlanes[i] = planes[i];
    585 	}
    586 
    587 	ps.numPortalPlanes = numPlanes;
    588 	ps.rect = tr.viewDef->scissor;
    589 
    590 	// if outside the world, mark everything
    591 	if ( tr.viewDef->areaNum < 0 ){
    592 		for ( int i = 0; i < numPortalAreas; i++ ) {
    593 			areaScreenRect[i] = tr.viewDef->scissor;
    594 			AddAreaToView( i, &ps );
    595 		}
    596 	} else {
    597 		// flood out through portals, setting area viewCount
    598 		FloodViewThroughArea_r( origin, tr.viewDef->areaNum, &ps );
    599 	}
    600 }
    601 
    602 /*
    603 ===================
    604 idRenderWorldLocal::BuildConnectedAreas_r
    605 ===================
    606 */
    607 void idRenderWorldLocal::BuildConnectedAreas_r( int areaNum ) {
    608 	if ( tr.viewDef->connectedAreas[areaNum] ) {
    609 		return;
    610 	}
    611 
    612 	tr.viewDef->connectedAreas[areaNum] = true;
    613 
    614 	// flood through all non-blocked portals
    615 	portalArea_t * area = &portalAreas[ areaNum ];
    616 	for ( const portal_t * portal = area->portals; portal; portal = portal->next ) {
    617 		if ( ( portal->doublePortal->blockingBits & PS_BLOCK_VIEW ) == 0 ) {
    618 			BuildConnectedAreas_r( portal->intoArea );
    619 		}
    620 	}
    621 }
    622 
    623 /*
    624 ===================
    625 idRenderWorldLocal::BuildConnectedAreas
    626 
    627 This is only valid for a given view, not all views in a frame
    628 ===================
    629 */
    630 void idRenderWorldLocal::BuildConnectedAreas() {
    631 	tr.viewDef->connectedAreas = (bool *)R_FrameAlloc( numPortalAreas * sizeof( tr.viewDef->connectedAreas[0] ) );
    632 
    633 	// if we are outside the world, we can see all areas
    634 	if ( tr.viewDef->areaNum == -1 ) {
    635 		for ( int i = 0; i < numPortalAreas; i++ ) {
    636 			tr.viewDef->connectedAreas[i] = true;
    637 		}
    638 		return;
    639 	}
    640 
    641 	// start with none visible, and flood fill from the current area
    642 	memset( tr.viewDef->connectedAreas, 0, numPortalAreas * sizeof( tr.viewDef->connectedAreas[0] ) );
    643 	BuildConnectedAreas_r( tr.viewDef->areaNum );
    644 }
    645 
    646 /*
    647 =============
    648 idRenderWorldLocal::FindViewLightsAndEntites
    649 
    650 All the modelrefs and lightrefs that are in visible areas
    651 will have viewEntitys and viewLights created for them.
    652 
    653 The scissorRects on the viewEntitys and viewLights may be empty if
    654 they were considered, but not actually visible.
    655 
    656 Entities and lights can have cached viewEntities / viewLights that
    657 will be used if the viewCount variable matches.
    658 =============
    659 */
    660 void idRenderWorldLocal::FindViewLightsAndEntities() {
    661 	SCOPED_PROFILE_EVENT( "FindViewLightsAndEntities" );
    662 
    663 	// bumping this counter invalidates cached viewLights / viewEntities,
    664 	// when a light or entity is next considered, it will create a new
    665 	// viewLight / viewEntity
    666 	tr.viewCount++;
    667 
    668 	// clear the visible lightDef and entityDef lists
    669 	tr.viewDef->viewLights = NULL;
    670 	tr.viewDef->viewEntitys = NULL;
    671 
    672 	// all areas are initially not visible, but each portal
    673 	// chain that leads to them will expand the visible rectangle
    674 	for ( int i = 0; i < numPortalAreas; i++ ) {
    675 		areaScreenRect[i].Clear();
    676 	}
    677 
    678 	// find the area to start the portal flooding in
    679 	if ( !r_usePortals.GetBool() ) {
    680 		// debug tool to force no portal culling
    681 		tr.viewDef->areaNum = -1;
    682 	} else {
    683 		tr.viewDef->areaNum = PointInArea( tr.viewDef->initialViewAreaOrigin );
    684 	}
    685 
    686 	// determine all possible connected areas for
    687 	// light-behind-door culling
    688 	BuildConnectedAreas();
    689 
    690 	// flow through all the portals and add models / lights
    691 	if ( r_singleArea.GetBool() ) {
    692 		// if debugging, only mark this area
    693 		// if we are outside the world, don't draw anything
    694 		if ( tr.viewDef->areaNum >= 0 ) {
    695 			static int lastPrintedAreaNum;
    696 			if ( tr.viewDef->areaNum != lastPrintedAreaNum ) {
    697 				lastPrintedAreaNum = tr.viewDef->areaNum;
    698 				common->Printf( "entering portal area %i\n", tr.viewDef->areaNum );
    699 			}
    700 
    701 			portalStack_t ps;
    702 			for ( int i = 0; i < 5; i++ ) {
    703 				ps.portalPlanes[i] = tr.viewDef->frustum[i];
    704 			}
    705 			ps.numPortalPlanes = 5;
    706 			ps.rect = tr.viewDef->scissor;
    707 
    708 			AddAreaToView( tr.viewDef->areaNum, &ps );
    709 		}
    710 	} else {
    711 		// note that the center of projection for flowing through portals may
    712 		// be a different point than initialViewAreaOrigin for subviews that
    713 		// may have the viewOrigin in a solid/invalid area
    714 		FlowViewThroughPortals( tr.viewDef->renderView.vieworg, 5, tr.viewDef->frustum );
    715 	}
    716 }
    717 
    718 /*
    719 =======================================================================
    720 
    721 Light linking into the BSP tree by flooding through portals
    722 
    723 =======================================================================
    724 */
    725 
    726 /*
    727 ===================
    728 idRenderWorldLocal::FloodLightThroughArea_r
    729 ===================
    730 */
    731 void idRenderWorldLocal::FloodLightThroughArea_r( idRenderLightLocal *light, int areaNum, 
    732 								 const portalStack_t *ps ) {
    733 	assert( ps != NULL ); // compiler warning
    734 	portal_t*		p = NULL;
    735 	float			d;
    736 	portalArea_t *	area = NULL;
    737 	const portalStack_t	*check = NULL, *firstPortalStack = NULL;
    738 	portalStack_t	newStack;
    739 	int				i, j;
    740 	idVec3			v1, v2;
    741 	int				addPlanes;
    742 	idFixedWinding	w;		// we won't overflow because MAX_PORTAL_PLANES = 20
    743 
    744 	area = &portalAreas[ areaNum ];
    745 
    746 	// add an areaRef
    747 	AddLightRefToArea( light, area );	
    748 
    749 	// go through all the portals
    750 	for ( p = area->portals; p; p = p->next ) {
    751 		// make sure this portal is facing away from the view
    752 		d = p->plane.Distance( light->globalLightOrigin );
    753 		if ( d < -0.1f ) {
    754 			continue;
    755 		}
    756 
    757 		// make sure the portal isn't in our stack trace,
    758 		// which would cause an infinite loop
    759 		for ( check = ps; check; check = check->next ) {
    760 			firstPortalStack = check;
    761 			if ( check->p == p ) {
    762 				break;		// don't recursively enter a stack
    763 			}
    764 		}
    765 		if ( check ) {
    766 			continue;	// already in stack
    767 		}
    768 
    769 		// if we are very close to the portal surface, don't bother clipping
    770 		// it, which tends to give epsilon problems that make the area vanish
    771 		if ( d < 1.0f ) {
    772 			// go through this portal
    773 			newStack = *ps;
    774 			newStack.p = p;
    775 			newStack.next = ps;
    776 			FloodLightThroughArea_r( light, p->intoArea, &newStack );
    777 			continue;
    778 		}
    779 
    780 		// clip the portal winding to all of the planes
    781 		w = *p->w;
    782 		for ( j = 0; j < ps->numPortalPlanes; j++ ) {
    783 			if ( !w.ClipInPlace( -ps->portalPlanes[j], 0 ) ) {
    784 				break;
    785 			}
    786 		}
    787 		if ( !w.GetNumPoints() ) {
    788 			continue;	// portal not visible
    789 		}
    790 		// also always clip to the original light planes, because they aren't
    791 		// necessarily extending to infinitiy like a view frustum
    792 		for ( j = 0; j < firstPortalStack->numPortalPlanes; j++ ) {
    793 			if ( !w.ClipInPlace( -firstPortalStack->portalPlanes[j], 0 ) ) {
    794 				break;
    795 			}
    796 		}
    797 		if ( !w.GetNumPoints() ) {
    798 			continue;	// portal not visible
    799 		}
    800 
    801 		// go through this portal
    802 		newStack.p = p;
    803 		newStack.next = ps;
    804 
    805 		// generate a set of clipping planes that will further restrict
    806 		// the visible view beyond just the scissor rect
    807 
    808 		addPlanes = w.GetNumPoints();
    809 		if ( addPlanes > MAX_PORTAL_PLANES ) {
    810 			addPlanes = MAX_PORTAL_PLANES;
    811 		}
    812 
    813 		newStack.numPortalPlanes = 0;
    814 		for ( i = 0; i < addPlanes; i++ ) {
    815 			j = i+1;
    816 			if ( j == w.GetNumPoints() ) {
    817 				j = 0;
    818 			}
    819 
    820 			v1 = light->globalLightOrigin - w[i].ToVec3();
    821 			v2 = light->globalLightOrigin - w[j].ToVec3();
    822 
    823 			newStack.portalPlanes[newStack.numPortalPlanes].Normal().Cross( v2, v1 );
    824 
    825 			// if it is degenerate, skip the plane
    826 			if ( newStack.portalPlanes[newStack.numPortalPlanes].Normalize() < 0.01f ) {
    827 				continue;
    828 			}
    829 			newStack.portalPlanes[newStack.numPortalPlanes].FitThroughPoint( light->globalLightOrigin );
    830 
    831 			newStack.numPortalPlanes++;
    832 		}
    833 
    834 		FloodLightThroughArea_r( light, p->intoArea, &newStack );
    835 	}
    836 }
    837 
    838 /*
    839 =======================
    840 idRenderWorldLocal::FlowLightThroughPortals
    841 
    842 Adds an arearef in each area that the light center flows into.
    843 This can only be used for shadow casting lights that have a generated
    844 prelight, because shadows are cast from back side which may not be in visible areas.
    845 =======================
    846 */
    847 void idRenderWorldLocal::FlowLightThroughPortals( idRenderLightLocal *light ) {
    848 	// if the light origin areaNum is not in a valid area,
    849 	// the light won't have any area refs
    850 	if ( light->areaNum == -1 ) {
    851 		return;
    852 	}
    853 
    854 	idPlane frustumPlanes[6];
    855 	idRenderMatrix::GetFrustumPlanes( frustumPlanes, light->baseLightProject, true, true );
    856 
    857 	portalStack_t ps;
    858 	memset( &ps, 0, sizeof( ps ) );
    859 	ps.numPortalPlanes = 6;
    860 	for ( int i = 0; i < 6; i++ ) {
    861 		ps.portalPlanes[i] = -frustumPlanes[i];
    862 	}
    863 
    864 	FloodLightThroughArea_r( light, light->areaNum, &ps );
    865 }
    866 
    867 /*
    868 =======================================================================
    869 
    870 Portal State Management
    871 
    872 =======================================================================
    873 */
    874 
    875 /*
    876 ==============
    877 NumPortals
    878 ==============
    879 */
    880 int idRenderWorldLocal::NumPortals() const {
    881 	return numInterAreaPortals;
    882 }
    883 
    884 /*
    885 ==============
    886 FindPortal
    887 
    888 Game code uses this to identify which portals are inside doors.
    889 Returns 0 if no portal contacts the bounds
    890 ==============
    891 */
    892 qhandle_t idRenderWorldLocal::FindPortal( const idBounds &b ) const {
    893 	int				i, j;
    894 	idBounds		wb;
    895 	doublePortal_t	*portal;
    896 	idWinding		*w;
    897 
    898 	for ( i = 0; i < numInterAreaPortals; i++ ) {
    899 		portal = &doublePortals[i];
    900 		w = portal->portals[0]->w;
    901 
    902 		wb.Clear();
    903 		for ( j = 0; j < w->GetNumPoints(); j++ ) {
    904 			wb.AddPoint( (*w)[j].ToVec3() );
    905 		}
    906 		if ( wb.IntersectsBounds( b ) ) {
    907 			return i + 1;
    908 		}
    909 	}
    910 
    911 	return 0;
    912 }
    913 
    914 /*
    915 =============
    916 FloodConnectedAreas
    917 =============
    918 */
    919 void idRenderWorldLocal::FloodConnectedAreas( portalArea_t *area, int portalAttributeIndex ) {
    920 	if ( area->connectedAreaNum[portalAttributeIndex] == connectedAreaNum ) {
    921 		return;
    922 	}
    923 	area->connectedAreaNum[portalAttributeIndex] = connectedAreaNum;
    924 
    925 	for ( portal_t *p = area->portals; p != NULL; p = p->next ) {
    926 		if ( !(p->doublePortal->blockingBits & (1<<portalAttributeIndex) ) ) {
    927 			FloodConnectedAreas( &portalAreas[p->intoArea], portalAttributeIndex );
    928 		}
    929 	}
    930 }
    931 
    932 /*
    933 ==============
    934 AreasAreConnected
    935 ==============
    936 */
    937 bool idRenderWorldLocal::AreasAreConnected( int areaNum1, int areaNum2, portalConnection_t connection ) const {
    938 	if ( areaNum1 == -1 || areaNum2 == -1 ) {
    939 		return false;
    940 	}
    941 	if ( areaNum1 > numPortalAreas || areaNum2 > numPortalAreas || areaNum1 < 0 || areaNum2 < 0 ) {
    942 		common->Error( "idRenderWorldLocal::AreAreasConnected: bad parms: %i, %i", areaNum1, areaNum2 );
    943 	}
    944 
    945 	int	attribute = 0;
    946 
    947 	int	intConnection = (int)connection;
    948 
    949 	while ( intConnection > 1 ) {
    950 		attribute++;
    951 		intConnection >>= 1;
    952 	}
    953 	if ( attribute >= NUM_PORTAL_ATTRIBUTES || ( 1 << attribute ) != (int)connection ) {
    954 		common->Error( "idRenderWorldLocal::AreasAreConnected: bad connection number: %i\n", (int)connection );
    955 	}
    956 
    957 	return portalAreas[areaNum1].connectedAreaNum[attribute] == portalAreas[areaNum2].connectedAreaNum[attribute];
    958 }
    959 
    960 /*
    961 ==============
    962 SetPortalState
    963 
    964 doors explicitly close off portals when shut
    965 ==============
    966 */
    967 void idRenderWorldLocal::SetPortalState( qhandle_t portal, int blockTypes ) {
    968 	if ( portal == 0 ) {
    969 		return;
    970 	}
    971 
    972 	if ( portal < 1 || portal > numInterAreaPortals ) {
    973 		common->Error( "SetPortalState: bad portal number %i", portal );
    974 	}
    975 	int	old = doublePortals[portal-1].blockingBits;
    976 	if ( old == blockTypes ) {
    977 		return;
    978 	}
    979 	doublePortals[portal-1].blockingBits = blockTypes;
    980 
    981 	// leave the connectedAreaGroup the same on one side,
    982 	// then flood fill from the other side with a new number for each changed attribute
    983 	for ( int i = 0; i < NUM_PORTAL_ATTRIBUTES; i++ ) {
    984 		if ( ( old ^ blockTypes ) & ( 1 << i ) ) {
    985 			connectedAreaNum++;
    986 			FloodConnectedAreas( &portalAreas[doublePortals[portal-1].portals[1]->intoArea], i );
    987 		}
    988 	}
    989 
    990 	if ( common->WriteDemo() ) {
    991 		common->WriteDemo()->WriteInt( DS_RENDER );
    992 		common->WriteDemo()->WriteInt( DC_SET_PORTAL_STATE );
    993 		common->WriteDemo()->WriteInt( portal );
    994 		common->WriteDemo()->WriteInt( blockTypes );
    995 	}
    996 }
    997 
    998 /*
    999 ==============
   1000 GetPortalState
   1001 ==============
   1002 */
   1003 int idRenderWorldLocal::GetPortalState( qhandle_t portal ) {
   1004 	if ( portal == 0 ) {
   1005 		return 0;
   1006 	}
   1007 
   1008 	if ( portal < 1 || portal > numInterAreaPortals ) {
   1009 		common->Error( "GetPortalState: bad portal number %i", portal );
   1010 	}
   1011 
   1012 	return doublePortals[portal-1].blockingBits;
   1013 }
   1014 
   1015 /*
   1016 =====================
   1017 idRenderWorldLocal::ShowPortals
   1018 
   1019 Debugging tool, won't work correctly with SMP or when mirrors are present
   1020 =====================
   1021 */
   1022 void idRenderWorldLocal::ShowPortals() {
   1023 	int			i, j;
   1024 	portalArea_t	*area;
   1025 	portal_t	*p;
   1026 	idWinding	*w;
   1027 
   1028 	// flood out through portals, setting area viewCount
   1029 	for ( i = 0; i < numPortalAreas; i++ ) {
   1030 		area = &portalAreas[i];
   1031 		if ( area->viewCount != tr.viewCount ) {
   1032 			continue;
   1033 		}
   1034 		for ( p = area->portals; p; p = p->next ) {
   1035 			w = p->w;
   1036 			if ( !w ) {
   1037 				continue;
   1038 			}
   1039 
   1040 			if ( portalAreas[ p->intoArea ].viewCount != tr.viewCount ) {
   1041 				// red = can't see
   1042 				GL_Color( 1, 0, 0 );
   1043 			} else {
   1044 				// green = see through
   1045 				GL_Color( 0, 1, 0 );
   1046 			}
   1047 
   1048 			qglBegin( GL_LINE_LOOP );
   1049 			for ( j = 0; j < w->GetNumPoints(); j++ ) {
   1050 				qglVertex3fv( (*w)[j].ToFloatPtr() );
   1051 			}
   1052 			qglEnd();
   1053 		}
   1054 	}
   1055 }