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