/// <summary> /// IsObjectVisible() function for portals. /// </summary> /// <remarks> /// Everything needs to be updated spatially before this function is /// called including portal corners, frustum planes, etc. /// </remarks> /// <param name="portal"> /// The <see cref="Portal"/> to check visibility against. /// </param> /// <param name="culledBy"> /// The <see cref="FrustumPlane"/> that the Portal is in. /// </param> /// <returns> /// true if the Portal is visible. /// </returns> public bool IsObjectVisible( Portal portal, out FrustumPlane culledBy ) { culledBy = FrustumPlane.None; // if portal isn't open, it's not visible if ( !portal.IsOpen ) { return false; } // check the extra frustum first if ( !this.extraCullingFrustum.IsObjectVisible( portal ) ) { return false; } // if portal is of type AABB or Sphere, then use simple bound check against planes if ( portal.Type == PORTAL_TYPE.PORTAL_TYPE_AABB ) { var aabb = new AxisAlignedBox( portal.getDerivedCorner( 0 ), portal.getDerivedCorner( 1 ) ); return base.IsObjectVisible( aabb, out culledBy ); } else if ( portal.Type == PORTAL_TYPE.PORTAL_TYPE_SPHERE ) { return base.IsObjectVisible( portal.getDerivedSphere(), out culledBy ); } // check if the portal norm is facing the camera Vector3 cameraToPortal = portal.getDerivedCP() - DerivedPosition; Vector3 portalDirection = portal.getDerivedDirection(); Real dotProduct = cameraToPortal.Dot( portalDirection ); if ( dotProduct > 0 ) { // portal is faced away from camera return false; } // check against regular frustum planes bool visible_flag; if ( null != CullFrustum ) { // For each frustum plane, see if all points are on the negative side // If so, object is not visible // NOTE: We skip the NEAR plane (plane #0) because Portals need to // be visible no matter how close you get to them. for ( int plane = 1; plane < 6; ++plane ) { // set the visible flag to false visible_flag = false; // Skip far plane if infinite view frustum if ( (FrustumPlane)plane == FrustumPlane.Far && _farDistance == 0 ) { continue; } // we have to check each corner of the portal for ( int corner = 0; corner < 4; corner++ ) { PlaneSide side = CullFrustum.FrustumPlanes[ plane ].GetSide( portal.getDerivedCorner( corner ) ); if ( side != PlaneSide.Negative ) { visible_flag = true; } } // if the visible_flag is still false, then this plane // culled all the portal points if ( visible_flag == false ) { // ALL corners on negative side therefore out of view if ( culledBy != FrustumPlane.None ) { culledBy = (FrustumPlane)plane; } return false; } } } else { // Make any pending updates to the calculated frustum planes UpdateFrustumPlanes(); // For each frustum plane, see if all points are on the negative side // If so, object is not visible // NOTE: We skip the NEAR plane (plane #0) because Portals need to // be visible no matter how close you get to them. // BUGBUG: This can cause a false positive situation when a portal is // behind the camera but close. This could be fixed by having another // culling plane at the camera location with normal same as camera direction. for ( int plane = 1; plane < 6; ++plane ) { // set the visible flag to false visible_flag = false; // Skip far plane if infinite view frustum if ( (FrustumPlane)plane == FrustumPlane.Far && _farDistance == 0 ) { continue; } // we have to check each corner of the portal for ( int corner = 0; corner < 4; corner++ ) { PlaneSide side = _planes[ plane ].GetSide( portal.getDerivedCorner( corner ) ); if ( side != PlaneSide.Negative ) { visible_flag = true; } } // if the visible_flag is still false, then this plane // culled all the portal points if ( visible_flag == false ) { // ALL corners on negative side therefore out of view if ( culledBy != FrustumPlane.None ) { culledBy = (FrustumPlane)plane; } return false; } } } // no plane culled all the portal points and the norm // was facing the camera, so this portal is visible return true; }
/// <summary> /// IsObjectVisible() function for portals. /// </summary> /// <remarks> /// Everything needs to be updated spatially before this function is /// called including portal corners, frustum planes, etc. /// </remarks> /// <param name="portal"> /// The <see cref="Portal"/> to check visibility against. /// </param> /// <returns> /// true if the Portal is visible. /// </returns> public bool IsObjectVisible( Portal portal ) { // if portal isn't open, it's not visible if ( !portal.IsOpen ) { return false; } // if the frustum has no planes, just return true if ( mActiveCullingPlanes.Count == 0 ) { return true; } // check if this portal is already in the list of active culling planes (avoid // infinite recursion case) foreach ( PCPlane plane in mActiveCullingPlanes ) { if ( plane.Portal == portal ) { return false; } } // if portal is of type AABB or Sphere, then use simple bound check against planes if ( portal.Type == PORTAL_TYPE.PORTAL_TYPE_AABB ) { AxisAlignedBox aabb = new AxisAlignedBox(); aabb.SetExtents( portal.getDerivedCorner( 0 ), portal.getDerivedCorner( 1 ) ); return IsObjectVisible( aabb ); } else if ( portal.Type == PORTAL_TYPE.PORTAL_TYPE_SPHERE ) { return IsObjectVisible( portal.getDerivedSphere() ); } // check if the portal norm is facing the frustum Vector3 frustumToPortal = portal.getDerivedCP() - mOrigin; Vector3 portalDirection = portal.getDerivedDirection(); Real dotProduct = frustumToPortal.Dot( portalDirection ); if ( dotProduct > 0 ) { // portal is faced away from Frustum return false; } // check against frustum culling planes bool visible_flag; // Check originPlane if told to if ( mUseOriginPlane ) { // set the visible flag to false visible_flag = false; // we have to check each corner of the portal for ( int corner = 0; corner < 4; corner++ ) { PlaneSide side = mOriginPlane.GetSide( portal.getDerivedCorner( corner ) ); if ( side != PlaneSide.Negative ) { visible_flag = true; } } // if the visible_flag is still false, then the origin plane // culled all the portal points if ( visible_flag == false ) { // ALL corners on negative side therefore out of view return false; } } // For each active culling plane, see if all portal points are on the negative // side. If so, the portal is not visible foreach ( PCPlane plane in mActiveCullingPlanes ) { visible_flag = false; // we have to check each corner of the portal for ( int corner = 0; corner < 4; corner++ ) { PlaneSide side = plane.GetSide( portal.getDerivedCorner( corner ) ); if ( side != PlaneSide.Negative ) { visible_flag = true; } } // if the visible_flag is still false, then this plane // culled all the portal points if ( visible_flag == false ) { // ALL corners on negative side therefore out of view return false; } } // no plane culled all the portal points and the norm // was facing the frustum, so this portal is visible return true; }
// calculate culling planes from portal and frustum // origin and add to list of culling planes // NOTE: returns 0 if portal was completely culled by existing planes // returns > 0 if culling planes are added (# is planes added) public int AddPortalCullingPlanes( Portal portal ) { int addedcullingplanes = 0; // If portal is of type aabb or sphere, add a plane which is same as frustum // origin plane (ie. redundant). We do this because we need the plane as a flag // to prevent infinite recursion if ( portal.Type == PORTAL_TYPE.PORTAL_TYPE_AABB || portal.Type == PORTAL_TYPE.PORTAL_TYPE_SPHERE ) { PCPlane newPlane = GetUnusedCullingPlane(); newPlane.SetFromAxiomPlane( mOriginPlane ); newPlane.Portal = portal; mActiveCullingPlanes.Add( newPlane ); addedcullingplanes++; return addedcullingplanes; } // For portal Quads: Up to 4 planes can be added by the sides of a portal quad. // Each plane is created from 2 corners (world space) of the portal and the // frustum origin (world space). int i, j; PlaneSide pt0_side, pt1_side; bool visible; for ( i = 0; i < 4; i++ ) { // first check if both corners are outside of one of the existing planes j = i + 1; if ( j > 3 ) { j = 0; } visible = true; foreach ( PCPlane plane in mActiveCullingPlanes ) { pt0_side = plane.GetSide( portal.getDerivedCorner( i ) ); pt1_side = plane.GetSide( portal.getDerivedCorner( j ) ); if ( pt0_side == PlaneSide.Negative && pt1_side == PlaneSide.Negative ) { // the portal edge was actually completely culled by one of culling planes visible = false; } } if ( visible ) { // add the plane created from the two portal corner points and the frustum location // to the culling plane PCPlane newPlane = GetUnusedCullingPlane(); if ( projType == Projection.Orthographic ) // use camera direction if projection is orthographic. { newPlane.Redefine( portal.getDerivedCorner( j ) + mOriginPlane.Normal, portal.getDerivedCorner( j ), portal.getDerivedCorner( i ) ); } else { newPlane.Redefine( mOrigin, portal.getDerivedCorner( j ), portal.getDerivedCorner( i ) ); } newPlane.Portal = portal; mActiveCullingPlanes.Add( newPlane ); addedcullingplanes++; } } // if we added ANY planes from the quad portal, we should add the plane of the // portal itself as an additional culling plane. if ( addedcullingplanes > 0 ) { PCPlane newPlane = GetUnusedCullingPlane(); newPlane.Redefine( portal.getDerivedCorner( 2 ), portal.getDerivedCorner( 1 ), portal.getDerivedCorner( 0 ) ); newPlane.Portal = portal; mActiveCullingPlanes.Add( newPlane ); addedcullingplanes++; } return addedcullingplanes; }
// calculate culling planes from portal and frustum // origin and add to list of culling planes // NOTE: returns 0 if portal was completely culled by existing planes // returns > 0 if culling planes are added (# is planes added) public int AddPortalCullingPlanes(Portal portal) { int addedcullingplanes = 0; // If portal is of type aabb or sphere, add a plane which is same as frustum // origin plane (ie. redundant). We do this because we need the plane as a flag // to prevent infinite recursion if (portal.Type == PORTAL_TYPE.PORTAL_TYPE_AABB || portal.Type == PORTAL_TYPE.PORTAL_TYPE_SPHERE) { PCPlane newPlane = GetUnusedCullingPlane(); newPlane.SetFromAxiomPlane(this.mOriginPlane); newPlane.Portal = portal; this.mActiveCullingPlanes.Add(newPlane); addedcullingplanes++; return(addedcullingplanes); } // For portal Quads: Up to 4 planes can be added by the sides of a portal quad. // Each plane is created from 2 corners (world space) of the portal and the // frustum origin (world space). int i, j; PlaneSide pt0_side, pt1_side; bool visible; for (i = 0; i < 4; i++) { // first check if both corners are outside of one of the existing planes j = i + 1; if (j > 3) { j = 0; } visible = true; foreach (PCPlane plane in this.mActiveCullingPlanes) { pt0_side = plane.GetSide(portal.getDerivedCorner(i)); pt1_side = plane.GetSide(portal.getDerivedCorner(j)); if (pt0_side == PlaneSide.Negative && pt1_side == PlaneSide.Negative) { // the portal edge was actually completely culled by one of culling planes visible = false; } } if (visible) { // add the plane created from the two portal corner points and the frustum location // to the culling plane PCPlane newPlane = GetUnusedCullingPlane(); if (this.projType == Projection.Orthographic) // use camera direction if projection is orthographic. { newPlane.Redefine(portal.getDerivedCorner(j) + this.mOriginPlane.Normal, portal.getDerivedCorner(j), portal.getDerivedCorner(i)); } else { newPlane.Redefine(this.mOrigin, portal.getDerivedCorner(j), portal.getDerivedCorner(i)); } newPlane.Portal = portal; this.mActiveCullingPlanes.Add(newPlane); addedcullingplanes++; } } // if we added ANY planes from the quad portal, we should add the plane of the // portal itself as an additional culling plane. if (addedcullingplanes > 0) { PCPlane newPlane = GetUnusedCullingPlane(); newPlane.Redefine(portal.getDerivedCorner(2), portal.getDerivedCorner(1), portal.getDerivedCorner(0)); newPlane.Portal = portal; this.mActiveCullingPlanes.Add(newPlane); addedcullingplanes++; } return(addedcullingplanes); }
/// <summary> /// IsObjectVisible() function for portals. /// </summary> /// <remarks> /// Everything needs to be updated spatially before this function is /// called including portal corners, frustum planes, etc. /// </remarks> /// <param name="portal"> /// The <see cref="Portal"/> to check visibility against. /// </param> /// <returns> /// true if the Portal is visible. /// </returns> public bool IsObjectVisible(Portal portal) { // if portal isn't open, it's not visible if (!portal.IsOpen) { return(false); } // if the frustum has no planes, just return true if (this.mActiveCullingPlanes.Count == 0) { return(true); } // check if this portal is already in the list of active culling planes (avoid // infinite recursion case) foreach (PCPlane plane in this.mActiveCullingPlanes) { if (plane.Portal == portal) { return(false); } } // if portal is of type AABB or Sphere, then use simple bound check against planes if (portal.Type == PORTAL_TYPE.PORTAL_TYPE_AABB) { var aabb = new AxisAlignedBox(); aabb.SetExtents(portal.getDerivedCorner(0), portal.getDerivedCorner(1)); return(IsObjectVisible(aabb)); } else if (portal.Type == PORTAL_TYPE.PORTAL_TYPE_SPHERE) { return(IsObjectVisible(portal.getDerivedSphere())); } // check if the portal norm is facing the frustum Vector3 frustumToPortal = portal.getDerivedCP() - this.mOrigin; Vector3 portalDirection = portal.getDerivedDirection(); Real dotProduct = frustumToPortal.Dot(portalDirection); if (dotProduct > 0) { // portal is faced away from Frustum return(false); } // check against frustum culling planes bool visible_flag; // Check originPlane if told to if (this.mUseOriginPlane) { // set the visible flag to false visible_flag = false; // we have to check each corner of the portal for (int corner = 0; corner < 4; corner++) { PlaneSide side = this.mOriginPlane.GetSide(portal.getDerivedCorner(corner)); if (side != PlaneSide.Negative) { visible_flag = true; } } // if the visible_flag is still false, then the origin plane // culled all the portal points if (visible_flag == false) { // ALL corners on negative side therefore out of view return(false); } } // For each active culling plane, see if all portal points are on the negative // side. If so, the portal is not visible foreach (PCPlane plane in this.mActiveCullingPlanes) { visible_flag = false; // we have to check each corner of the portal for (int corner = 0; corner < 4; corner++) { PlaneSide side = plane.GetSide(portal.getDerivedCorner(corner)); if (side != PlaneSide.Negative) { visible_flag = true; } } // if the visible_flag is still false, then this plane // culled all the portal points if (visible_flag == false) { // ALL corners on negative side therefore out of view return(false); } } // no plane culled all the portal points and the norm // was facing the frustum, so this portal is visible return(true); }
/* This function check if *this* portal "crossed over" the other portal. */ public bool crossedPortal(Portal otherPortal) { // Only check if portal is open if (otherPortal.mOpen) { // we model both portals as line swept spheres (mPrevDerivedCP to mDerivedCP). // intersection test is then between the capsules. // BUGBUG! This routine needs to check for case where one or both objects // don't move - resulting in simple sphere tests // BUGBUG! If one (or both) portals are aabb's this is REALLY not accurate. Capsule portalCapsule, otherPortalCapsule; portalCapsule = new Capsule(); portalCapsule.Set(getPrevDerivedCP(), getDerivedCP(), getRadius()); otherPortalCapsule = new Capsule(); otherPortalCapsule.Set(otherPortal.mPrevDerivedCP, otherPortal.mDerivedCP, otherPortal.mRadius); if (portalCapsule.Intersects(otherPortalCapsule)) { // the portal intersected the other portal at some time from last frame to this frame. // Now check if this portal "crossed" the other portal switch (otherPortal.Type) { case PORTAL_TYPE.PORTAL_TYPE_QUAD: // a crossing occurs if the "side" of the final position of this portal compared // to the final position of the other portal is negative AND the initial position // of this portal compared to the initial position of the other portal is non-negative // NOTE: This function assumes that this portal is the smaller portal potentially crossing // over the otherPortal which is larger. if (otherPortal.getDerivedPlane().GetSide(this.mDerivedCP) == PlaneSide.Negative && otherPortal.getPrevDerivedPlane().GetSide(this.mPrevDerivedCP) != PlaneSide.Negative) { // crossing occurred! return(true); } break; case PORTAL_TYPE.PORTAL_TYPE_AABB: { // for aabb's we check if the center point went from being inside to being outside // the aabb (or vice versa) for crossing. var aabb = new AxisAlignedBox(otherPortal.getDerivedCorner(0), otherPortal.getDerivedCorner(1)); //bool previousInside = aabb.contains(mPrevDerivedCP); bool currentInside = aabb.Contains(this.mDerivedCP); if (otherPortal.getDerivedDirection() == Vector3.UnitZ) { // portal norm is "outward" pointing, look for going from outside to inside //if (previousInside == false && if (currentInside == true) { return(true); } } else { // portal norm is "inward" pointing, look for going from inside to outside //if (previousInside == true && if (currentInside == false) { return(true); } } } break; case PORTAL_TYPE.PORTAL_TYPE_SPHERE: { // for spheres we check if the center point went from being inside to being outside // the sphere surface (or vice versa) for crossing. //Real previousDistance2 = mPrevDerivedCP.squaredDistance(otherPortal->getPrevDerivedCP()); Real currentDistance2 = this.mDerivedCP.DistanceSquared(otherPortal.getDerivedCP()); Real mRadius2 = System.Math.Sqrt(otherPortal.getRadius()); if (otherPortal.getDerivedDirection() == Vector3.UnitZ) { // portal norm is "outward" pointing, look for going from outside to inside //if (previousDistance2 >= mRadius2 && if (currentDistance2 < mRadius2) { return(true); } } else { // portal norm is "inward" pointing, look for going from inside to outside //if (previousDistance2 < mRadius2 && if (currentDistance2 >= mRadius2) { return(true); } } } break; } } } // there was no crossing of the portal by this portal. It might be touching // the other portal (but we don't care currently) return(false); }
/* This function check if *this* portal "crossed over" the other portal. */ public bool crossedPortal( Portal otherPortal ) { // Only check if portal is open if ( otherPortal.mOpen ) { // we model both portals as line swept spheres (mPrevDerivedCP to mDerivedCP). // intersection test is then between the capsules. // BUGBUG! This routine needs to check for case where one or both objects // don't move - resulting in simple sphere tests // BUGBUG! If one (or both) portals are aabb's this is REALLY not accurate. Capsule portalCapsule, otherPortalCapsule; portalCapsule = new Capsule(); portalCapsule.Set( this.getPrevDerivedCP(), this.getDerivedCP(), this.getRadius() ); otherPortalCapsule = new Capsule(); otherPortalCapsule.Set( otherPortal.mPrevDerivedCP, otherPortal.mDerivedCP, otherPortal.mRadius ); if ( portalCapsule.Intersects( otherPortalCapsule ) ) { // the portal intersected the other portal at some time from last frame to this frame. // Now check if this portal "crossed" the other portal switch ( otherPortal.Type ) { case PORTAL_TYPE.PORTAL_TYPE_QUAD: // a crossing occurs if the "side" of the final position of this portal compared // to the final position of the other portal is negative AND the initial position // of this portal compared to the initial position of the other portal is non-negative // NOTE: This function assumes that this portal is the smaller portal potentially crossing // over the otherPortal which is larger. if ( otherPortal.getDerivedPlane().GetSide( mDerivedCP ) == PlaneSide.Negative && otherPortal.getPrevDerivedPlane().GetSide( mPrevDerivedCP ) != PlaneSide.Negative ) { // crossing occurred! return true; } break; case PORTAL_TYPE.PORTAL_TYPE_AABB: { // for aabb's we check if the center point went from being inside to being outside // the aabb (or vice versa) for crossing. AxisAlignedBox aabb = new AxisAlignedBox( otherPortal.getDerivedCorner( 0 ), otherPortal.getDerivedCorner( 1 ) ); //bool previousInside = aabb.contains(mPrevDerivedCP); bool currentInside = aabb.Contains( mDerivedCP ); if ( otherPortal.getDerivedDirection() == Vector3.UnitZ ) { // portal norm is "outward" pointing, look for going from outside to inside //if (previousInside == false && if ( currentInside == true ) { return true; } } else { // portal norm is "inward" pointing, look for going from inside to outside //if (previousInside == true && if ( currentInside == false ) { return true; } } } break; case PORTAL_TYPE.PORTAL_TYPE_SPHERE: { // for spheres we check if the center point went from being inside to being outside // the sphere surface (or vice versa) for crossing. //Real previousDistance2 = mPrevDerivedCP.squaredDistance(otherPortal->getPrevDerivedCP()); Real currentDistance2 = mDerivedCP.DistanceSquared( otherPortal.getDerivedCP() ); Real mRadius2 = System.Math.Sqrt( otherPortal.getRadius() ); if ( otherPortal.getDerivedDirection() == Vector3.UnitZ ) { // portal norm is "outward" pointing, look for going from outside to inside //if (previousDistance2 >= mRadius2 && if ( currentDistance2 < mRadius2 ) { return true; } } else { // portal norm is "inward" pointing, look for going from inside to outside //if (previousDistance2 < mRadius2 && if ( currentDistance2 >= mRadius2 ) { return true; } } } break; } } } // there was no crossing of the portal by this portal. It might be touching // the other portal (but we don't care currently) return false; }
/// <summary> /// IsObjectVisible() function for portals. /// </summary> /// <remarks> /// Everything needs to be updated spatially before this function is /// called including portal corners, frustum planes, etc. /// </remarks> /// <param name="portal"> /// The <see cref="Portal"/> to check visibility against. /// </param> /// <param name="culledBy"> /// The <see cref="FrustumPlane"/> that the Portal is in. /// </param> /// <returns> /// true if the Portal is visible. /// </returns> public bool IsObjectVisible(Portal portal, out FrustumPlane culledBy) { culledBy = FrustumPlane.None; // if portal isn't open, it's not visible if (!portal.IsOpen) { return(false); } // check the extra frustum first if (!this.extraCullingFrustum.IsObjectVisible(portal)) { return(false); } // if portal is of type AABB or Sphere, then use simple bound check against planes if (portal.Type == PORTAL_TYPE.PORTAL_TYPE_AABB) { var aabb = new AxisAlignedBox(portal.getDerivedCorner(0), portal.getDerivedCorner(1)); return(base.IsObjectVisible(aabb, out culledBy)); } else if (portal.Type == PORTAL_TYPE.PORTAL_TYPE_SPHERE) { return(base.IsObjectVisible(portal.getDerivedSphere(), out culledBy)); } // check if the portal norm is facing the camera Vector3 cameraToPortal = portal.getDerivedCP() - DerivedPosition; Vector3 portalDirection = portal.getDerivedDirection(); Real dotProduct = cameraToPortal.Dot(portalDirection); if (dotProduct > 0) { // portal is faced away from camera return(false); } // check against regular frustum planes bool visible_flag; if (null != CullFrustum) { // For each frustum plane, see if all points are on the negative side // If so, object is not visible // NOTE: We skip the NEAR plane (plane #0) because Portals need to // be visible no matter how close you get to them. for (int plane = 1; plane < 6; ++plane) { // set the visible flag to false visible_flag = false; // Skip far plane if infinite view frustum if ((FrustumPlane)plane == FrustumPlane.Far && _farDistance == 0) { continue; } // we have to check each corner of the portal for (int corner = 0; corner < 4; corner++) { PlaneSide side = CullFrustum.FrustumPlanes[plane].GetSide(portal.getDerivedCorner(corner)); if (side != PlaneSide.Negative) { visible_flag = true; } } // if the visible_flag is still false, then this plane // culled all the portal points if (visible_flag == false) { // ALL corners on negative side therefore out of view if (culledBy != FrustumPlane.None) { culledBy = (FrustumPlane)plane; } return(false); } } } else { // Make any pending updates to the calculated frustum planes UpdateFrustumPlanes(); // For each frustum plane, see if all points are on the negative side // If so, object is not visible // NOTE: We skip the NEAR plane (plane #0) because Portals need to // be visible no matter how close you get to them. // BUGBUG: This can cause a false positive situation when a portal is // behind the camera but close. This could be fixed by having another // culling plane at the camera location with normal same as camera direction. for (int plane = 1; plane < 6; ++plane) { // set the visible flag to false visible_flag = false; // Skip far plane if infinite view frustum if ((FrustumPlane)plane == FrustumPlane.Far && _farDistance == 0) { continue; } // we have to check each corner of the portal for (int corner = 0; corner < 4; corner++) { PlaneSide side = _planes[plane].GetSide(portal.getDerivedCorner(corner)); if (side != PlaneSide.Negative) { visible_flag = true; } } // if the visible_flag is still false, then this plane // culled all the portal points if (visible_flag == false) { // ALL corners on negative side therefore out of view if (culledBy != FrustumPlane.None) { culledBy = (FrustumPlane)plane; } return(false); } } } // no plane culled all the portal points and the norm // was facing the camera, so this portal is visible return(true); }