/* ======================= FlowViewThroughPortals Finds viewLights and viewEntities by flowing from an origin through the visible portals. origin point can see into. The planes array defines a volume (positive sides facing in) that should contain the origin, such as a view frustum or a point light box. Zero planes assumes an unbounded volume. ======================= */ void idRenderWorldLocal::FlowViewThroughPortals( const idVec3 origin, int numPlanes, const idPlane *planes ) { portalStack_t ps; int i; ps.next = NULL; ps.p = NULL; for ( i = 0 ; i < numPlanes ; i++ ) { ps.portalPlanes[i] = planes[i]; } ps.numPortalPlanes = numPlanes; ps.rect = tr.viewDef->scissor; if ( tr.viewDef->areaNum < 0 ){ for ( i = 0; i < numPortalAreas; i++ ) { areaScreenRect[i] = tr.viewDef->scissor; } // if outside the world, mark everything for ( i = 0 ; i < numPortalAreas ; i++ ) { AddAreaRefs( i, &ps ); } } else { for ( i = 0; i < numPortalAreas; i++ ) { areaScreenRect[i].Clear(); } // flood out through portals, setting area viewCount FloodViewThroughArea_r( origin, tr.viewDef->areaNum, &ps ); } }
/* ======================= idRenderWorldLocal::FlowViewThroughPortals Finds viewLights and viewEntities by flowing from an origin through the visible portals that the origin point can see into. The planes array defines a volume with the planes pointing outside the volume. Zero planes assumes an unbounded volume. ======================= */ void idRenderWorldLocal::FlowViewThroughPortals( const idVec3& origin, int numPlanes, const idPlane* planes ) { portalStack_t ps; ps.next = NULL; ps.p = NULL; assert( numPlanes <= MAX_PORTAL_PLANES ); for( int i = 0; i < numPlanes; i++ ) { ps.portalPlanes[i] = planes[i]; } ps.numPortalPlanes = numPlanes; ps.rect = tr.viewDef->scissor; // if outside the world, mark everything if( tr.viewDef->areaNum < 0 ) { for( int i = 0; i < numPortalAreas; i++ ) { areaScreenRect[i] = tr.viewDef->scissor; AddAreaToView( i, &ps ); } } else { // flood out through portals, setting area viewCount FloodViewThroughArea_r( origin, tr.viewDef->areaNum, &ps ); } }
/* =================== FloodViewThroughArea_r =================== */ void idRenderWorldLocal::FloodViewThroughArea_r( const idVec3 origin, int areaNum, const struct portalStack_s *ps ) { portal_t* p; float d; portalArea_t * area; const portalStack_t *check; portalStack_t newStack; int i, j; idVec3 v1, v2; int addPlanes; idFixedWinding w; // we won't overflow because MAX_PORTAL_PLANES = 20 area = &portalAreas[ areaNum ]; // cull models and lights to the current collection of planes AddAreaRefs( areaNum, ps ); if ( areaScreenRect[areaNum].IsEmpty() ) { areaScreenRect[areaNum] = ps->rect; } else { areaScreenRect[areaNum].Union( ps->rect ); } // go through all the portals for ( p = area->portals; p; p = p->next ) { // an enclosing door may have sealed the portal off if ( p->doublePortal->blockingBits & PS_BLOCK_VIEW ) { continue; } // make sure this portal is facing away from the view d = p->plane.Distance( origin ); if ( d < -0.1f ) { continue; } // make sure the portal isn't in our stack trace, // which would cause an infinite loop for ( check = ps; check; check = check->next ) { if ( check->p == p ) { break; // don't recursively enter a stack } } if ( check ) { continue; // already in stack } // if we are very close to the portal surface, don't bother clipping // it, which tends to give epsilon problems that make the area vanish if ( d < 1.0f ) { // go through this portal newStack = *ps; newStack.p = p; newStack.next = ps; FloodViewThroughArea_r( origin, p->intoArea, &newStack ); continue; } // clip the portal winding to all of the planes w = *p->w; for ( j = 0; j < ps->numPortalPlanes; j++ ) { if ( !w.ClipInPlace( -ps->portalPlanes[j], 0 ) ) { break; } } if ( !w.GetNumPoints() ) { continue; // portal not visible } // see if it is fogged out if ( PortalIsFoggedOut( p ) ) { continue; } // go through this portal newStack.p = p; newStack.next = ps; // find the screen pixel bounding box of the remaining portal // so we can scissor things outside it newStack.rect = ScreenRectFromWinding( &w, &tr.identitySpace ); // slop might have spread it a pixel outside, so trim it back newStack.rect.Intersect( ps->rect ); // generate a set of clipping planes that will further restrict // the visible view beyond just the scissor rect addPlanes = w.GetNumPoints(); if ( addPlanes > MAX_PORTAL_PLANES ) { addPlanes = MAX_PORTAL_PLANES; } newStack.numPortalPlanes = 0; for ( i = 0; i < addPlanes; i++ ) { j = i+1; if ( j == w.GetNumPoints() ) { j = 0; } v1 = origin - w[i].ToVec3(); v2 = origin - w[j].ToVec3(); newStack.portalPlanes[newStack.numPortalPlanes].Normal().Cross( v2, v1 ); // if it is degenerate, skip the plane if ( newStack.portalPlanes[newStack.numPortalPlanes].Normalize() < 0.01f ) { continue; } newStack.portalPlanes[newStack.numPortalPlanes].FitThroughPoint( origin ); newStack.numPortalPlanes++; } // the last stack plane is the portal plane newStack.portalPlanes[newStack.numPortalPlanes] = p->plane; newStack.numPortalPlanes++; FloodViewThroughArea_r( origin, p->intoArea, &newStack ); } }