I'm trying to implement frustum culling into my game, and although I've realized it's already built in Irrlicht, I'd like to do it myself because I need the information which node was culled and which not.
The problem is that from certain angles, it culls even those parts that should be visible. Here are some pictures of it:
http://www.pport.fw.hu/frustum0.PNG
http://www.pport.fw.hu/frustum1.PNG
http://www.pport.fw.hu/frustum2.PNG
And here's the code:
Code: Select all
//! CScreenFrustum class
class CScreenFrustum
{
public:
//! constructor
CScreenFrustum(scene::ISceneManager* Smgr, scene::ICameraSceneNode* Cam);
//! isNodeInside
bool isNodeInside(scene::IMeshSceneNode* Node);
private:
//! transform
void transform(const core::matrix4& mat);
//! rebuild
void rebuild();
//! isPointInside
bool isPointInside(core::vector3df Point);
// local instances
scene::ISceneManager* smgr;
scene::ICameraSceneNode* cam;
// 0,0 W,0 0,H W,H
core::line3df r00, r10, r01, r11;
// screen frustum
core::plane3df plane[6];
// BB
core::aabbox3df BB;
}; // end class CScreenFrustum
Code: Select all
//! constructor
CScreenFrustum::CScreenFrustum(scene::ISceneManager* Smgr, scene::ICameraSceneNode* Cam)
{
// local instances
smgr = Smgr;
cam = Cam;
// screen coordinates
scene::ISceneCollisionManager* cmgr = smgr->getSceneCollisionManager();
core::dimension2d<s32> screen = smgr->getVideoDriver()->getScreenSize();
// plane points in 3D
r00 = cmgr->getRayFromScreenCoordinates(core::position2d<s32>(0, 0), cam);
r10 = cmgr->getRayFromScreenCoordinates(core::position2d<s32>(screen.Width, 0), cam);
r01 = cmgr->getRayFromScreenCoordinates(core::position2d<s32>(0, screen.Height), cam);
r11 = cmgr->getRayFromScreenCoordinates(core::position2d<s32>(screen.Width, screen.Height), cam);
// adjusting with aspect ratio (Irrlicht sets the start coordinates to the camera
core::matrix4 mat = cam->getAbsoluteTransformation();
float aspr = cam->getAspectRatio();
r00.start = core::vector3df(-aspr, 1, 0);
r10.start = core::vector3df( aspr, 1, 0);
r01.start = core::vector3df(-aspr, -1, 0);
r11.start = core::vector3df( aspr, -1, 0);
mat.transformVect(r00.start);
mat.transformVect(r10.start);
mat.transformVect(r01.start);
mat.transformVect(r11.start);
// BB
BB.reset(cam->getAbsolutePosition());
BB.addInternalPoint(r00.start);
BB.addInternalPoint(r10.start);
BB.addInternalPoint(r01.start);
BB.addInternalPoint(r11.start);
BB.addInternalPoint(r00.end);
BB.addInternalPoint(r10.end);
BB.addInternalPoint(r01.end);
BB.addInternalPoint(r11.end);
}
//! isPointInside
bool CScreenFrustum::isPointInside(core::vector3df Point)
{
for (int i = 0; i < 6; ++i)
if (plane[i].classifyPointRelation(Point) == core::ISREL3D_BACK) return false;
return true;
}
//! isNodeInside
bool CScreenFrustum::isNodeInside(scene::IMeshSceneNode* Node)
{
// Quick tests ----
// trans node BB
core::aabbox3df nodeTransBB = Node->getTransformedBoundingBox();
// if the camera is inside the nodeTRBB
if (nodeTransBB.isPointInside(cam->getAbsolutePosition())) return true;
// if nodeTransBB is not inside in frustum BB
if (!BB.intersectsWithBox(nodeTransBB)) return false;
// frustum test ----
// rebuild planes
rebuild();
// transform the frustum to the node's absolute transformation
core::matrix4 invTrans(Node->getAbsoluteTransformation());
invTrans.makeInverse();
transform(invTrans);
// get the points of nodeBB
core::aabbox3df nodeBB = Node->getBoundingBox();
core::vector3df p[8];
nodeBB.getEdges(&p[0]);
// if at least one point is inside, the node is inside
for (int i = 0; i < 8; ++i)
if (isPointInside(p[i])) return true;
return false;
}
//! transform
/// transform the planes with matrix
void CScreenFrustum::transform(const core::matrix4& mat)
{
for (int i = 0; i < 6; ++i) mat.transformPlane(plane[i]);
}
//! rebuild
/// rebuild planes
void CScreenFrustum::rebuild()
{
// planes
plane[0].setPlane(r10.start, r00.start, r01.start); // near
plane[1].setPlane(r00.end, r10.end, r01.end); // far
plane[2].setPlane(r01.end, r11.end, r11.start); // bottom
plane[3].setPlane(r00.start, r10.start, r10.end); // top
plane[4].setPlane(r00.start, r00.end, r01.end); // left
plane[5].setPlane(r10.end, r10.start, r11.start); // right
}
Thanks in advane!
PI