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