/// <summary> /// This may be entered multiple times with different planes if more than /// one portal sees into the area. /// </summary> /// <param name="areaNumber"></param> /// <param name="portalStack"></param> private void AddAreaReferences(int areaNumber, PortalStack portalStack) { // mark the viewCount, so r_showPortals can display the // considered portals _portalAreas[areaNumber].ViewCount = idE.RenderSystem.ViewCount; // add the models and lights, using more precise culling to the planes AddAreaEntityReferences(areaNumber, portalStack); // TODO: AddAreaLightRefs( areaNum, ps ); }
/// <summary> /// Any models that are visible through the current portalStack will have their scissor. /// </summary> /// <param name="areaNumber"></param> /// <param name="portalStack"></param> private void AddAreaEntityReferences(int areaNumber, PortalStack portalStack) { PortalArea area = _portalAreas[areaNumber]; for(AreaReference areaRef = area.EntityReference.NextArea; areaRef != area.EntityReference; areaRef = areaRef.NextArea) { idRenderEntity entity = areaRef.Entity; // debug tool to allow viewing of only one entity at a time if((idE.CvarSystem.GetInteger("r_singleEntity") > 0) && (idE.CvarSystem.GetInteger("r_singleEntity") != entity.EntityIndex)) { continue; } // remove decals that are completely faded away // TODO: R_FreeEntityDefFadedDecals( entity, tr.viewDef->renderView.time ); // check for completely suppressing the model if(idE.CvarSystem.GetBool("r_skipSuppress") == false) { if(entity.Parameters.SuppressSurfaceInViewID == idE.RenderSystem.ViewDefinition.RenderView.ViewID) { continue; } else if(entity.Parameters.AllowSurfaceInViewID != idE.RenderSystem.ViewDefinition.RenderView.ViewID) { continue; } } // cull reference bounds if(CullEntityByPortals(entity, portalStack) == true) { // we are culled out through this portal chain, but it might // still be visible through others continue; } ViewEntity viewEntity = SetEntityDefViewEntity(entity); // possibly expand the scissor rect viewEntity.ScissorRectangle.Union(portalStack.Rectangle); } }
private void FloodViewThroughArea(Vector3 origin, int areaNumber, PortalStack portalStack) { int i, j; idWinding winding; // we won't overflow because MAX_PORTAL_PLANES = 20 PortalStack check; PortalStack newStack; PortalArea area = _portalAreas[areaNumber]; // cull models and lights to the current collection of planes AddAreaReferences(areaNumber, portalStack); if(_areaScreenRect[areaNumber].IsEmpty == true) { _areaScreenRect[areaNumber] = portalStack.Rectangle; } else { _areaScreenRect[areaNumber].Union(portalStack.Rectangle); } // go through all the portals Portal portal; for(portal = area.Portals; portal != null; portal = portal.Next) { // an enclosing door may have sealed the portal off if((portal.DoublePortal.BlockingBits & PortalConnection.BlockView) == PortalConnection.BlockView) { continue; } // make sure this portal is facing away from the view float d = portal.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 = portalStack; check != null; check = check.Next) { if(check.Portal == portal) { break; // don't recursively enter a stack } } if(check != null) { 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 = portalStack; newStack.Portal = portal; newStack.Next = portalStack; FloodViewThroughArea(origin, portal.IntoArea, newStack); continue; } // clip the portal winding to all of the planes winding = portal.Winding; for(j = 0; j < portalStack.PortalPlaneCount; j++) { Plane neg = new Plane(-portalStack.PortalPlanes[j].Normal, -portalStack.PortalPlanes[j].D); if(winding.ClipInPlace(neg, 0) == false) { break; } } if(winding.PointCount == 0) { continue; // portal not visible } // see if it is fogged out if(PortalIsFoggedOut(portal) == true) { continue; } // go through this portal newStack = new PortalStack(); newStack.Portal = portal; newStack.Next = portalStack; // find the screen pixel bounding box of the remaining portal // so we can scissor things outside it newStack.Rectangle = ScreenRectangleFromWinding(winding, idE.RenderSystem.IdentitySpace); // slop might have spread it a pixel outside, so trim it back newStack.Rectangle.Intersect(portalStack.Rectangle); // generate a set of clipping planes that will further restrict // the visible view beyond just the scissor rect int addPlanes = winding.PointCount; if(addPlanes > idE.MaxPortalPlanes) { addPlanes = idE.MaxPortalPlanes; } newStack.PortalPlaneCount = 0; for(i = 0; i < addPlanes; i++) { j = i + 1; if(j == winding.PointCount) { j = 0; } Vector3 v1 = origin - winding[i]; Vector3 v2 = origin - winding[j]; newStack.PortalPlanes[newStack.PortalPlaneCount].Normal = Vector3.Cross( v2, v1 ); // if it is degenerate, skip the plane newStack.PortalPlanes[newStack.PortalPlaneCount].Normalize(); if(newStack.PortalPlanes[newStack.PortalPlaneCount].Normal.Length() < 0.01f) { continue; } newStack.PortalPlanes[newStack.PortalPlaneCount].FitThroughPoint(origin); newStack.PortalPlaneCount++; } // the last stack plane is the portal plane newStack.PortalPlanes[newStack.PortalPlaneCount] = portal.Plane; newStack.PortalPlaneCount++; FloodViewThroughArea(origin, portal.IntoArea, newStack); } }
/// <summary> /// 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. /// </summary> /// <param name="origin"></param> /// <param name="planes"></param> private void FlowViewThroughPortals(Vector3 origin, int planeCount, Plane[] planes) { View viewDef = idE.RenderSystem.ViewDefinition; PortalStack portalStack = new PortalStack(); portalStack.Rectangle = viewDef.Scissor; portalStack.PortalPlaneCount = planeCount; for(int i = 0; i < planeCount; i++) { portalStack.PortalPlanes[i] = planes[i]; } if(viewDef.AreaNumber < 0) { for(int i = 0; i < _portalAreaCount; i++) { _areaScreenRect[i] = viewDef.Scissor; } // if outside the world, mark everything for(int i = 0; i < _portalAreaCount; i++) { AddAreaReferences(i, portalStack); } } else { for(int i = 0; i < _portalAreaCount; i++) { _areaScreenRect[i] = new idScreenRect(); } // flood out through portals, setting area viewCount FloodViewThroughArea(origin, viewDef.AreaNumber, portalStack); } }
/// <summary> /// Return true if the entity reference bounds do not intersect the current portal chain. /// </summary> /// <param name="entity"></param> /// <param name="portalStack"></param> /// <returns></returns> private bool CullEntityByPortals(idRenderEntity entity, PortalStack portalStack) { if(idE.CvarSystem.GetBool("r_useEntityCulling") == false) { return false; } // try to cull the entire thing using the reference bounds. // we do not yet do callbacks or dynamic model creation, // because we want to do all touching of the model after // we have determined all the lights that may effect it, // which optimizes cache usage return idHelper.CullLocalBox(entity.ReferenceBounds, entity.ModelMatrix, portalStack.PortalPlaneCount, portalStack.PortalPlanes); }