2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
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.
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.
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/>.
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.
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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
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.
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;
48 typedef struct portalStack_s {
50 const struct portalStack_s *next;
55 idPlane portalPlanes[MAX_PORTAL_PLANES+1];
56 // positive side is outside the visible frustum
60 //====================================================================
65 idRenderWorldLocal::ScreenRectForWinding
68 idScreenRect idRenderWorldLocal::ScreenRectFromWinding( const idWinding *w, viewEntity_t *space ) {
73 float windowX, windowY;
76 for ( i = 0 ; i < w->GetNumPoints() ; i++ ) {
77 R_LocalPointToGlobal( space->modelMatrix, (*w)[i].ToVec3(), v );
78 R_GlobalToNormalizedDeviceCoordinates( v, ndc );
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 );
83 r.AddPoint( windowX, windowY );
96 bool idRenderWorldLocal::PortalIsFoggedOut( const portal_t *p ) {
97 idRenderLightLocal *ldef;
102 ldef = p->doublePortal->fogLight;
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 );
112 lightShader->EvaluateRegisters( regs, ldef->parms.shaderParms, tr.viewDef, ldef->parms.referenceSound );
114 const shaderStage_t *stage = lightShader->GetStage(0);
116 float alpha = regs[ stage->color.registers[3] ];
119 // if they left the default value on, set a fog distance of 500
122 if ( alpha <= 1.0f ) {
123 a = -0.5f / DEFAULT_FOG_DISTANCE;
125 // otherwise, distance = alpha color
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];
135 for ( i = 0 ; i < w->GetNumPoints() ; i++ ) {
138 d = forward.Distance( (*w)[i].ToVec3() );
140 return false; // a point not clipped off
149 FloodViewThroughArea_r
152 void idRenderWorldLocal::FloodViewThroughArea_r( const idVec3 origin, int areaNum,
153 const struct portalStack_s *ps ) {
157 const portalStack_t *check;
158 portalStack_t newStack;
162 idFixedWinding w; // we won't overflow because MAX_PORTAL_PLANES = 20
164 area = &portalAreas[ areaNum ];
166 // cull models and lights to the current collection of planes
167 AddAreaRefs( areaNum, ps );
169 if ( areaScreenRect[areaNum].IsEmpty() ) {
170 areaScreenRect[areaNum] = ps->rect;
172 areaScreenRect[areaNum].Union( ps->rect );
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
178 if ( p->doublePortal->blockingBits & PS_BLOCK_VIEW ) {
182 // make sure this portal is facing away from the view
183 d = p->plane.Distance( origin );
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
196 continue; // already in stack
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
203 // go through this portal
207 FloodViewThroughArea_r( origin, p->intoArea, &newStack );
211 // clip the portal winding to all of the planes
213 for ( j = 0; j < ps->numPortalPlanes; j++ ) {
214 if ( !w.ClipInPlace( -ps->portalPlanes[j], 0 ) ) {
218 if ( !w.GetNumPoints() ) {
219 continue; // portal not visible
222 // see if it is fogged out
223 if ( PortalIsFoggedOut( p ) ) {
227 // go through this portal
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 );
235 // slop might have spread it a pixel outside, so trim it back
236 newStack.rect.Intersect( ps->rect );
238 // generate a set of clipping planes that will further restrict
239 // the visible view beyond just the scissor rect
241 addPlanes = w.GetNumPoints();
242 if ( addPlanes > MAX_PORTAL_PLANES ) {
243 addPlanes = MAX_PORTAL_PLANES;
246 newStack.numPortalPlanes = 0;
247 for ( i = 0; i < addPlanes; i++ ) {
249 if ( j == w.GetNumPoints() ) {
253 v1 = origin - w[i].ToVec3();
254 v2 = origin - w[j].ToVec3();
256 newStack.portalPlanes[newStack.numPortalPlanes].Normal().Cross( v2, v1 );
258 // if it is degenerate, skip the plane
259 if ( newStack.portalPlanes[newStack.numPortalPlanes].Normalize() < 0.01f ) {
262 newStack.portalPlanes[newStack.numPortalPlanes].FitThroughPoint( origin );
264 newStack.numPortalPlanes++;
267 // the last stack plane is the portal plane
268 newStack.portalPlanes[newStack.numPortalPlanes] = p->plane;
269 newStack.numPortalPlanes++;
271 FloodViewThroughArea_r( origin, p->intoArea, &newStack );
276 =======================
277 FlowViewThroughPortals
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 =======================
285 void idRenderWorldLocal::FlowViewThroughPortals( const idVec3 origin, int numPlanes, const idPlane *planes ) {
292 for ( i = 0 ; i < numPlanes ; i++ ) {
293 ps.portalPlanes[i] = planes[i];
296 ps.numPortalPlanes = numPlanes;
297 ps.rect = tr.viewDef->scissor;
299 if ( tr.viewDef->areaNum < 0 ){
301 for ( i = 0; i < numPortalAreas; i++ ) {
302 areaScreenRect[i] = tr.viewDef->scissor;
305 // if outside the world, mark everything
306 for ( i = 0 ; i < numPortalAreas ; i++ ) {
307 AddAreaRefs( i, &ps );
311 for ( i = 0; i < numPortalAreas; i++ ) {
312 areaScreenRect[i].Clear();
315 // flood out through portals, setting area viewCount
316 FloodViewThroughArea_r( origin, tr.viewDef->areaNum, &ps );
320 //==================================================================================================
325 FloodLightThroughArea_r
328 void idRenderWorldLocal::FloodLightThroughArea_r( idRenderLightLocal *light, int areaNum,
329 const struct portalStack_s *ps ) {
333 const portalStack_t *check, *firstPortalStack;
334 portalStack_t newStack;
338 idFixedWinding w; // we won't overflow because MAX_PORTAL_PLANES = 20
340 area = &portalAreas[ areaNum ];
343 AddLightRefToArea( light, area );
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 );
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
362 continue; // already in stack
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
368 // go through this portal
372 FloodLightThroughArea_r( light, p->intoArea, &newStack );
376 // clip the portal winding to all of the planes
378 for ( j = 0; j < ps->numPortalPlanes; j++ ) {
379 if ( !w.ClipInPlace( -ps->portalPlanes[j], 0 ) ) {
383 if ( !w.GetNumPoints() ) {
384 continue; // portal not visible
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 ) ) {
393 if ( !w.GetNumPoints() ) {
394 continue; // portal not visible
397 // go through this portal
401 // generate a set of clipping planes that will further restrict
402 // the visible view beyond just the scissor rect
404 addPlanes = w.GetNumPoints();
405 if ( addPlanes > MAX_PORTAL_PLANES ) {
406 addPlanes = MAX_PORTAL_PLANES;
409 newStack.numPortalPlanes = 0;
410 for ( i = 0; i < addPlanes; i++ ) {
412 if ( j == w.GetNumPoints() ) {
416 v1 = light->globalLightOrigin - w[i].ToVec3();
417 v2 = light->globalLightOrigin - w[j].ToVec3();
419 newStack.portalPlanes[newStack.numPortalPlanes].Normal().Cross( v2, v1 );
421 // if it is degenerate, skip the plane
422 if ( newStack.portalPlanes[newStack.numPortalPlanes].Normalize() < 0.01f ) {
425 newStack.portalPlanes[newStack.numPortalPlanes].FitThroughPoint( light->globalLightOrigin );
427 newStack.numPortalPlanes++;
430 FloodLightThroughArea_r( light, p->intoArea, &newStack );
436 =======================
437 FlowLightThroughPortals
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 =======================
444 void idRenderWorldLocal::FlowLightThroughPortals( idRenderLightLocal *light ) {
447 const idVec3 origin = light->globalLightOrigin;
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 ) {
455 memset( &ps, 0, sizeof( ps ) );
457 ps.numPortalPlanes = 6;
458 for ( i = 0 ; i < 6 ; i++ ) {
459 ps.portalPlanes[i] = light->frustum[i];
462 FloodLightThroughArea_r( light, light->areaNum, &ps );
465 //======================================================================================================
469 idRenderWorldLocal::FloodFrustumAreas_r
472 areaNumRef_t *idRenderWorldLocal::FloodFrustumAreas_r( const idFrustum &frustum, const int areaNum, const idBounds &bounds, areaNumRef_t *areas ) {
474 portalArea_t *portalArea;
478 portalArea = &portalAreas[ areaNum ];
480 // go through all the portals
481 for ( p = portalArea->portals; p; p = p->next ) {
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 ) {
493 // the frustum origin must be at the front of the portal plane
494 if ( p->plane.Side( frustum.GetOrigin(), 0.1f ) == SIDE_BACK ) {
498 // the frustum must cross the portal plane
499 if ( frustum.PlaneSide( p->plane, 0.0f ) != PLANESIDE_CROSS ) {
503 // get the bounds for the portal winding projected in the frustum
504 frustum.ProjectionBounds( *p->w, newBounds );
506 newBounds.IntersectSelf( bounds );
508 if ( newBounds[0][0] > newBounds[1][0] || newBounds[0][1] > newBounds[1][1] || newBounds[0][2] > newBounds[1][2] ) {
512 newBounds[1][0] = frustum.GetFarDistance();
514 a = areaNumRefAllocator.Alloc();
515 a->areaNum = p->intoArea;
519 areas = FloodFrustumAreas_r( frustum, p->intoArea, newBounds, areas );
527 idRenderWorldLocal::FloodFrustumAreas
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.
533 areaNumRef_t *idRenderWorldLocal::FloodFrustumAreas( const idFrustum &frustum, areaNumRef_t *areas ) {
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 );
541 for ( a = areas; a; a = a->next ) {
542 areas = FloodFrustumAreas_r( frustum, a->areaNum, bounds, areas );
550 =======================================================================
552 R_FindViewLightsAndEntities
554 =======================================================================
561 Return true if the entity reference bounds do not intersect the current portal chain.
564 bool idRenderWorldLocal::CullEntityByPortals( const idRenderEntityLocal *entity, const portalStack_t *ps ) {
566 if ( !r_useEntityCulling.GetBool() ) {
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 ) ) {
587 Any models that are visible through the current portalStack will
591 void idRenderWorldLocal::AddAreaEntityRefs( int areaNum, const portalStack_t *ps ) {
592 areaReference_t *ref;
593 idRenderEntityLocal *entity;
598 area = &portalAreas[ areaNum ];
600 for ( ref = area->entityRefs.areaNext ; ref != &area->entityRefs ; ref = ref->areaNext ) {
601 entity = ref->entity;
603 // debug tool to allow viewing of only one entity at a time
604 if ( r_singleEntity.GetInteger() >= 0 && r_singleEntity.GetInteger() != entity->index ) {
608 // remove decals that are completely faded away
609 R_FreeEntityDefFadedDecals( entity, tr.viewDef->renderView.time );
611 // check for completely suppressing the model
612 if ( !r_skipSuppress.GetBool() ) {
613 if ( entity->parms.suppressSurfaceInViewID
614 && entity->parms.suppressSurfaceInViewID == tr.viewDef->renderView.viewID ) {
617 if ( entity->parms.allowSurfaceInViewID
618 && entity->parms.allowSurfaceInViewID != tr.viewDef->renderView.viewID ) {
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
630 vEnt = R_SetEntityDefViewEntity( entity );
632 // possibly expand the scissor rect
633 vEnt->scissorRect.Union( ps->rect );
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.
645 bool idRenderWorldLocal::CullLightByPortals( const idRenderLightLocal *light, const portalStack_t *ps ) {
647 const srfTriangles_t *tri;
649 idFixedWinding w; // we won't overflow because MAX_PORTAL_PLANES = 20
651 if ( r_useLightCulling.GetInteger() == 0 ) {
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 ) {
666 // get the exact winding for this side
667 const idWinding *ow = light->frustumWindings[i];
669 // projected lights may have one of the frustums degenerated
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] ) ) {
683 if ( w.GetNumPoints() ) {
684 // part of the winding is visible through the portalStack,
685 // so the light is not culled
689 // none of the light surfaces were visible
694 // simple point check against each plane
695 tri = light->frustumTris;
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 );
702 break; // point is inside this plane
705 if ( j == tri->numVerts ) {
706 // all points were outside one of the planes
707 tr.pc.c_box_cull_out++;
720 This is the only point where lights get added to the viewLights list
723 void idRenderWorldLocal::AddAreaLightRefs( int areaNum, const portalStack_t *ps ) {
724 areaReference_t *lref;
726 idRenderLightLocal *light;
729 area = &portalAreas[ areaNum ];
731 for ( lref = area->lightRefs.areaNext ; lref != &area->lightRefs ; lref = lref->areaNext ) {
734 // debug tool to allow viewing of only one light at a time
735 if ( r_singleLight.GetInteger() >= 0 && r_singleLight.GetInteger() != light->index ) {
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 ] ) {
748 if ( CullLightByPortals( light, ps ) ) {
749 // we are culled out through this portal chain, but it might
750 // still be visible through others
754 vLight = R_SetLightDefViewLight( light );
756 // expand the scissor rect
757 vLight->scissorRect.Union( ps->rect );
765 This may be entered multiple times with different planes
766 if more than one portal sees into the area
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;
774 // add the models and lights, using more precise culling to the planes
775 AddAreaEntityRefs( areaNum, ps );
776 AddAreaLightRefs( areaNum, ps );
781 BuildConnectedAreas_r
784 void idRenderWorldLocal::BuildConnectedAreas_r( int areaNum ) {
788 if ( tr.viewDef->connectedAreas[areaNum] ) {
792 tr.viewDef->connectedAreas[areaNum] = true;
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 );
807 This is only valid for a given view, not all views in a frame
810 void idRenderWorldLocal::BuildConnectedAreas( void ) {
813 tr.viewDef->connectedAreas = (bool *)R_FrameAlloc( numPortalAreas
814 * sizeof( tr.viewDef->connectedAreas[0] ) );
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;
824 // start with none visible, and flood fill from the current area
825 memset( tr.viewDef->connectedAreas, 0, numPortalAreas * sizeof( tr.viewDef->connectedAreas[0] ) );
826 BuildConnectedAreas_r( tr.viewDef->areaNum );
831 FindViewLightsAndEntites
833 All the modelrefs and lightrefs that are in visible areas
834 will have viewEntitys and viewLights created for them.
836 The scissorRects on the viewEntitys and viewLights may be empty if
837 they were considered, but not actually visible.
840 void idRenderWorldLocal::FindViewLightsAndEntities( void ) {
841 // clear the visible lightDef and entityDef lists
842 tr.viewDef->viewLights = NULL;
843 tr.viewDef->viewEntitys = NULL;
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;
850 tr.viewDef->areaNum = PointInArea( tr.viewDef->initialViewAreaOrigin );
853 // determine all possible connected areas for
854 // light-behind-door culling
855 BuildConnectedAreas();
857 // bump the view count, invalidating all
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 ) {
868 static int lastPrintedAreaNum;
870 if ( tr.viewDef->areaNum != lastPrintedAreaNum ) {
871 lastPrintedAreaNum = tr.viewDef->areaNum;
872 common->Printf( "entering portal area %i\n", tr.viewDef->areaNum );
875 for ( i = 0 ; i < 5 ; i++ ) {
876 ps.portalPlanes[i] = tr.viewDef->frustum[i];
878 ps.numPortalPlanes = 5;
879 ps.rect = tr.viewDef->scissor;
881 AddAreaRefs( tr.viewDef->areaNum, &ps );
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
887 FlowViewThroughPortals( tr.viewDef->renderView.vieworg, 5, tr.viewDef->frustum );
896 int idRenderWorldLocal::NumPortals( void ) const {
897 return numInterAreaPortals;
904 Game code uses this to identify which portals are inside doors.
905 Returns 0 if no portal contacts the bounds
908 qhandle_t idRenderWorldLocal::FindPortal( const idBounds &b ) const {
911 doublePortal_t *portal;
914 for ( i = 0 ; i < numInterAreaPortals ; i++ ) {
915 portal = &doublePortals[i];
916 w = portal->portals[0]->w;
919 for ( j = 0 ; j < w->GetNumPoints() ; j++ ) {
920 wb.AddPoint( (*w)[j].ToVec3() );
922 if ( wb.IntersectsBounds( b ) ) {
935 void idRenderWorldLocal::FloodConnectedAreas( portalArea_t *area, int portalAttributeIndex ) {
936 if ( area->connectedAreaNum[portalAttributeIndex] == connectedAreaNum ) {
939 area->connectedAreaNum[portalAttributeIndex] = connectedAreaNum;
941 for ( portal_t *p = area->portals ; p ; p = p->next ) {
942 if ( !(p->doublePortal->blockingBits & (1<<portalAttributeIndex) ) ) {
943 FloodConnectedAreas( &portalAreas[p->intoArea], portalAttributeIndex );
954 bool idRenderWorldLocal::AreasAreConnected( int areaNum1, int areaNum2, portalConnection_t connection ) {
955 if ( areaNum1 == -1 || areaNum2 == -1 ) {
958 if ( areaNum1 > numPortalAreas || areaNum2 > numPortalAreas || areaNum1 < 0 || areaNum2 < 0 ) {
959 common->Error( "idRenderWorldLocal::AreAreasConnected: bad parms: %i, %i", areaNum1, areaNum2 );
964 int intConnection = (int)connection;
966 while ( intConnection > 1 ) {
970 if ( attribute >= NUM_PORTAL_ATTRIBUTES || ( 1 << attribute ) != (int)connection ) {
971 common->Error( "idRenderWorldLocal::AreasAreConnected: bad connection number: %i\n", (int)connection );
974 return portalAreas[areaNum1].connectedAreaNum[attribute] == portalAreas[areaNum2].connectedAreaNum[attribute];
982 doors explicitly close off portals when shut
985 void idRenderWorldLocal::SetPortalState( qhandle_t portal, int blockTypes ) {
990 if ( portal < 1 || portal > numInterAreaPortals ) {
991 common->Error( "SetPortalState: bad portal number %i", portal );
993 int old = doublePortals[portal-1].blockingBits;
994 if ( old == blockTypes ) {
997 doublePortals[portal-1].blockingBits = blockTypes;
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 ) ) {
1004 FloodConnectedAreas( &portalAreas[doublePortals[portal-1].portals[1]->intoArea], i );
1008 if ( session->writeDemo ) {
1009 session->writeDemo->WriteInt( DS_RENDER );
1010 session->writeDemo->WriteInt( DC_SET_PORTAL_STATE );
1011 session->writeDemo->WriteInt( portal );
1012 session->writeDemo->WriteInt( blockTypes );
1021 int idRenderWorldLocal::GetPortalState( qhandle_t portal ) {
1022 if ( portal == 0 ) {
1026 if ( portal < 1 || portal > numInterAreaPortals ) {
1027 common->Error( "GetPortalState: bad portal number %i", portal );
1030 return doublePortals[portal-1].blockingBits;
1034 =====================
1035 idRenderWorldLocal::ShowPortals
1037 Debugging tool, won't work correctly with SMP or when mirrors are present
1038 =====================
1040 void idRenderWorldLocal::ShowPortals() {
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 ) {
1052 for ( p = area->portals ; p ; p = p->next ) {
1058 if ( portalAreas[ p->intoArea ].viewCount != tr.viewCount ) {
1060 qglColor3f( 1, 0, 0 );
1062 // green = see through
1063 qglColor3f( 0, 1, 0 );
1066 qglBegin( GL_LINE_LOOP );
1067 for ( j = 0 ; j < w->GetNumPoints() ; j++ ) {
1068 qglVertex3fv( (*w)[j].ToFloatPtr() );