/* ======================= idRenderWorldLocal::FlowLightThroughPortals Adds an arearef in each area that the light center flows into. This can only be used for shadow casting lights that have a generated prelight, because shadows are cast from back side which may not be in visible areas. ======================= */ void idRenderWorldLocal::FlowLightThroughPortals( idRenderLightLocal *light ) { // if the light origin areaNum is not in a valid area, // the light won't have any area refs if ( light->areaNum == -1 ) { return; } idPlane frustumPlanes[6]; idRenderMatrix::GetFrustumPlanes( frustumPlanes, light->baseLightProject, true, true ); portalStack_t ps; memset( &ps, 0, sizeof( ps ) ); ps.numPortalPlanes = 6; for ( int i = 0; i < 6; i++ ) { ps.portalPlanes[i] = -frustumPlanes[i]; } FloodLightThroughArea_r( light, light->areaNum, &ps ); }
/* ======================= FlowLightThroughPortals Adds an arearef in each area that the light center flows into. This can only be used for shadow casting lights that have a generated prelight, because shadows are cast from back side which may not be in visible areas. ======================= */ void idRenderWorldLocal::FlowLightThroughPortals( idRenderLightLocal *light ) { portalStack_t ps; int i; // if the light origin areaNum is not in a valid area, // the light won't have any area refs if ( light->areaNum == -1 ) { return; } memset( &ps, 0, sizeof( ps ) ); ps.numPortalPlanes = 6; for ( i = 0 ; i < 6 ; i++ ) { ps.portalPlanes[i] = light->frustum[i]; } FloodLightThroughArea_r( light, light->areaNum, &ps ); }
/* =================== FloodLightThroughArea_r =================== */ void idRenderWorldLocal::FloodLightThroughArea_r( idRenderLightLocal *light, int areaNum, const struct portalStack_s *ps ) { portal_t* p; float d; portalArea_t * area; const portalStack_t *check, *firstPortalStack; 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 ]; // add an areaRef AddLightRefToArea( light, area ); // go through all the portals for ( p = area->portals; p; p = p->next ) { // make sure this portal is facing away from the view d = p->plane.Distance( light->globalLightOrigin ); 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 ) { firstPortalStack = check; 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; FloodLightThroughArea_r( light, 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 } // also always clip to the original light planes, because they aren't // necessarily extending to infinitiy like a view frustum for ( j = 0; j < firstPortalStack->numPortalPlanes; j++ ) { if ( !w.ClipInPlace( -firstPortalStack->portalPlanes[j], 0 ) ) { break; } } if ( !w.GetNumPoints() ) { continue; // portal not visible } // go through this portal newStack.p = p; newStack.next = ps; // 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 = light->globalLightOrigin - w[i].ToVec3(); v2 = light->globalLightOrigin - 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( light->globalLightOrigin ); newStack.numPortalPlanes++; } FloodLightThroughArea_r( light, p->intoArea, &newStack ); } }