#include "PlanarOccluder.h"
NiImplementRTTI(PlanarOccluder, Occluder);
//---------------------------------------------------------------------------
PlanarOccluder::PlanarOccluder()
{
}
//---------------------------------------------------------------------------
PlanarOccluder::PlanarOccluder(NiGeometry* pkPlane)
{
assert (pkPlane != 0);
m_spGeometry = pkPlane; //暂存。在后面的测试点是否在平面的Extent内使用这个Geo的Bounding Box
m_iCameraWhichSide = 0;
FillData(); //使用提供的Geo来创建一个代表他的Plane
}
//---------------------------------------------------------------------------
PlanarOccluder::~PlanarOccluder()
{
m_spGeometry = 0;
}
//---------------------------------------------------------------------------
void PlanarOccluder::Update(NiCamera* pkCamera)
{
// Determine which side of the occlusion planes the camera is on for
// fast dismissal during occlusion testing.
if (m_kLastTransform != m_spGeometry->GetWorldTransform()) //如果Geo运动。By Dynamic Plane Geo
FillData(); //产生一个Plane。 build the occlusion plane <Normal, PointOnPlane>
//All Calc in World Sys
m_iCameraWhichSide = m_kOcclusionPlane.WhichSide(
pkCamera->GetWorldLocation());
return;
}
//---------------------------------------------------------------------------
/**
kBound: The Bounding Box of an Object to be checked
*/
bool PlanarOccluder::IsOccluded(NiCamera* pkCamera, const NiBound& kBound)
{
NiPoint3 kTestLocation;
bool bCull = false;
//看看在Plane的Normal方向的物体BB的点是否与照相机在同一侧。make a pos on BB of this object in the dir of Plane normal
kTestLocation = kBound.GetCenter() + kBound.GetRadius() *
m_kOcclusionPlane.GetNormal();
//check to see if this point is on the same side as cam
bCull = m_kOcclusionPlane.WhichSide(kTestLocation) != m_iCameraWhichSide;
if (bCull)
{
//for the case where cam is on the negative side
kTestLocation = kBound.GetCenter() -
kBound.GetRadius() * m_kOcclusionPlane.GetNormal();
if (m_kOcclusionPlane.WhichSide(kTestLocation) == m_iCameraWhichSide)
bCull = false;
}
if (!bCull)
return false;
return CheckPlaneExtents(pkCamera, kBound); //如果物体的BB的所有极限点都在此平面内放回TRUE
}
//---------------------------------------------------------------------------
bool PlanarOccluder::CheckPlaneExtents(NiCamera* pkCamera,
const NiBound& kBound)
{
// The full bound falls on the opposite side of the plane from the
// camera. We just need to check and see if each point intersects the
// plane within it's extents.
NiPoint3 kCenter = kBound.GetCenter();
float fRadius = kBound.GetRadius();
NiPoint3 kTestPoint;
//创建物体BB的上下左右的极限点
// Bound offset up.
kTestPoint = kCenter + fRadius * pkCamera->GetWorldUpVector(); //Up Loc in world sys
if (!TestExtent(pkCamera, kTestPoint))
return false;
// Bound offset down.
kTestPoint = kCenter - fRadius * pkCamera->GetWorldUpVector();
if (!TestExtent(pkCamera, kTestPoint))
return false;
// Bound offset right.
kTestPoint = kCenter + fRadius * pkCamera->GetWorldRightVector();
if (!TestExtent(pkCamera, kTestPoint))
return false;
// Bound offset left.
kTestPoint = kCenter - fRadius * pkCamera->GetWorldRightVector();
if (!TestExtent(pkCamera, kTestPoint))
return false;
// If we reach here, then all the offset bound points are within the
// plane extents so the object is occluded.
return true;
}
//---------------------------------------------------------------------------
// If Point in the plane extent return TRUE
bool PlanarOccluder::TestExtent(NiCamera* pkCamera,
const NiPoint3& kTestPoint)
{
// Find intersection with the geometric occlusion plane.
NiPoint3 kCameraLoc = pkCamera->GetWorldTranslate(); //in World
NiPoint3 kPlaneNormal = m_kOcclusionPlane.GetNormal(); //in World
float fPlaneConstant = m_kOcclusionPlane.GetConstant();
NiPoint3 kTemp = (kTestPoint - kCameraLoc);
float fBottomTerm = kTemp.Dot(kPlaneNormal); //从照相机到测试点在Plane的Normal上的长度
//由于此测试点与照相机在此平面的不同侧.所以出现此情况只要照相机和测试点非常靠近此PlanePoint on Plane
if (fBottomTerm < 0.001f && fBottomTerm > -0.001f)
return true;
//照相机到平面在法向的距离
float fTopTerm = fPlaneConstant - kPlaneNormal.Dot(kCameraLoc);
float fU = fTopTerm / fBottomTerm;
//通过简单的三角形计算出交点的位置(世界坐标)
NiPoint3 kIntersection = kCameraLoc + fU * (kTestPoint - kCameraLoc);
// Project a vector from the plane center to kIntersection onto right
// and up vectors to make sure that the projection length is not
// greater than the stored magnitude. Since the world up and right
// are normalized, we can do this with a dot product.
//Intersection to Plane Center
//获得Plane的中心点位置
//计算出交点到中心点的距离向量
kTemp = kIntersection - m_spGeometry->GetWorldBound().GetCenter();
//检测这个距离向量是否超出平面的右方,上方的大小
float fRightMag = kTemp.Dot(m_kWorldRight);
// If this passes, then we're outside the geometric plane extents.
if (NiAbs(fRightMag) > m_fRightMag)
return false;
float fUpMag = kTemp.Dot(m_kWorldUp);
// If this passes, then we're outside the geometric plane extents.
if (NiAbs(fUpMag) > m_fUpMag) //m_fUpMag stores half plane height
return false;
return true;
}
//---------------------------------------------------------------------------
void PlanarOccluder::FillData()
{
NiGeometryData* pkData = m_spGeometry->GetModelData(); //m_spGeometry holds Plane
assert (pkData->GetVertexCount() >= 3);
NiPoint3 kPoint[3];
kPoint[0] = *pkData->GetVertices();
kPoint[1] = *(pkData->GetVertices() + 1);
kPoint[2] = *(pkData->GetVertices() + 2);
//transform all Vertices into World Sys
kPoint[0] = m_spGeometry->GetWorldTransform() * kPoint[0];
kPoint[1] = m_spGeometry->GetWorldTransform() * kPoint[1];
kPoint[2] = m_spGeometry->GetWorldTransform() * kPoint[2];
// CW Triangle
m_kWorldUp = kPoint[1] - kPoint[0]; //平面的上方向量
m_kWorldRight = kPoint[2] - kPoint[0]; //平面的右方向量
//m_fUpMag stores half plane height
m_fUpMag = m_kWorldUp.Unitize() * 0.5f;
//NiPoint3::Unitize() makes NiPoint3 into Unit Length and returns original length
m_fRightMag = m_kWorldRight.Unitize() * 0.5f;
NiPoint3 kNormal = m_kWorldUp.Cross(m_kWorldRight); //Left Handed Sys
m_kOcclusionPlane = NiPlane(kNormal, kPoint[0]); //Use the given Geo to create a Plane
m_kLastTransform = m_spGeometry->GetWorldTransform();
}
//---------------------------------------------------------------------------
NiGeometry* PlanarOccluder::GetGeometry()
{
return m_spGeometry;
}
//---------------------------------------------------------------------------