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