/// <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;
}