"stencil depth" sample from DirectX 8, showing overdraw :
- in red the pixels drawn once
- in green the pixels drawn twice
- in yellow the pixels drawn 3 times
- in blue the pixels drawn 4 times
node = graph's root node->SimpleTraversal(camera) node::SimpleTraversal(camera) // recursive function { node->Draw(camera) // draw the content of the node (= attached objects) for each child of the node // = each subnode child->SimpleTraversal(camera) for each 'visitor' // = subnode attached to another node, // but that extends (is visible) in this one visitor->SimpleTraversal(camera) } node::Draw(camera) { if there is at least one object in the node : activate the lights that affect the node for each object of the node if the object is a visitor and has already been drawn : skip it if the object is invisible : skip it if the object's bounding volume is outside the view frustim : skip the object object->Draw if the object is a visitor : add it to the list of already drawn objects in the camera }traversal with portals
node = room the camera is in node->PortalsTraversal(camera) node::PortalsTraversal(camera) { reinitialize the view frustum // = suppress planes added by the portals node->PortalsRecursiveTraversal(camera) } node::PortalsRecursiveTraversal(camera) { for each portal attached to the node dest = corresponding destination portal destnode = destination portal's node if the portal is disabled, skip it if the portal's bounding volume is outside the view frustum, skip the portal if the portal's bounding volume is outside the reduced frustum, skip the portal transform the camera's position to the portal's space if the camera is not facing the portal (= is not on the side pointed by its normal), skip the portal transform the portal's bounding volume (an AABB) to the camera's space calculate the equations of the planes formed by the camera and the edges of the transformed volume add these planes to the camera's view frustum if the portal has a 3D transformation (mirror, teleport...) calculate the camera's transformed position and orientation create a new camera with these parameters copy the planes of the current camera to the new one destnode->PortalsRecursiveTraversal(new camera) else destnode->PortalsRecursiceTraversal(camera) suppress the planes added to the camera by this portal // node->Draw(camera) // function described above // for each child of the node // = each subnode child->PortalsRecursiveTraversal(camera) for each 'visitor' // = subnode attached to another node // but that extends (is visible) in this one visitor->PortalsRecursiveTraversal(camera) }
/**********************************************************************************************************************/
/* GRAPH TRAVERSAL */
/**********************************************************************************************************************/
/************************************************ dwSimpleTraversal ***************************************************/
// traverse graph to render visible objects - init
// 22/04/01, M
// in : current camera,current frame number
// out: number of nodes traversed
// rem: simply follow the tree, no portals
/**********************************************************************************************************************/
DWORD CGraphNode::dwSimpleTraversal(const CCamera* lpCamera,const DWORD dwFrame)
{
DWORD dwNodes = 0;
vSimpleTraversal(lpCamera,dwFrame,&dwNodes);
return dwNodes;
}
/************************************************ vSimpleTraversal ****************************************************/
// traverse node & children (recursively)
// 22/04/01, M
// in : current camera,current frame number,node counter address
// out:
// rem: simply follow the tree, no portals
/**********************************************************************************************************************/
void CGraphNode::vSimpleTraversal(const CCamera* lpCamera,const DWORD dwFrame,DWORD* lpdwNodes)
{
CCamera* lpCam = const_cast<CCamera*>(lpCamera);
// if(m_dwTraversalSee == dwFrame) return; // already traversed; avoid infinite loop
m_dwTraversalSee = dwFrame;
(*lpdwNodes)++;
// traverse node
hrDraw(lpCamera);
// traverse children
listNodeIterator itChild = m_listChildren.begin(); // DON'T use lpGetFirstChild because of shared iterator !
while(itChild != m_listChildren.end())
{
CGraphNode* lpChild = *itChild;
CBVbase* lpBVNode = lpChild->lpGetBoundingVol();
CMat4x4 m4Node2World;
if(lpBVNode)
{
if(lpChild->boGetWorldMatrix(&m4Node2World))
lpBVNode->vSetTrf2World(&m4Node2World);
}
lpChild->vSimpleTraversal(lpCamera,dwFrame,lpdwNodes);
itChild++;
}
// traverse visitors
setNodeIterator itVisitor = m_setVisitors.begin();
while(itVisitor != m_setVisitors.end())
{
CGraphNode* lpVisitor = *itVisitor;
CBVbase* lpBVNode = lpVisitor->lpGetBoundingVol();
CMat4x4 m4Node2World;
if(lpBVNode)
{
if(lpVisitor->boGetWorldMatrix(&m4Node2World))
lpBVNode->vSetTrf2World(&m4Node2World);
}
lpVisitor->vSimpleTraversal(lpCamera,dwFrame,lpdwNodes);
itVisitor++;
}
}
/************************************************ dwTraversal *********************************************************/
// traverse graph to render visible objects - init
// 27/04/00, M
// in : current camera,current frame number
// out: number of nodes traversed
/**********************************************************************************************************************/
DWORD CGraphNode::dwTraversal(const CCamera* lpCamera,const DWORD dwFrame)
{
lpCamera->lpGetBVPlanes()->vSetTrf2World(lpCamera->lpm4Get2WorldMatrix());
lpCamera->lpGetBVPlanes()->vRemoveAll(); // planes are in camera space
DWORD dwNodes = 0;
vTraversal(lpCamera,dwFrame,&dwNodes);
return dwNodes;
}
/************************************************ vTraversal **********************************************************/
// traverse node & children (recursively)
// 27/04/00, M
// in : current camera,current frame number,node counter address
// out:
/**********************************************************************************************************************/
void CGraphNode::vTraversal(const CCamera* lpCamera,const DWORD dwFrame,DWORD* lpdwNodes)
{
CCamera* lpCam = const_cast<CCamera*>(lpCamera);
// if(m_dwTraversalSee == dwFrame) return; // already traversed; avoid infinite loop
m_dwTraversalSee = dwFrame;
(*lpdwNodes)++;
// traverse portals
listPortalIterator itPortal = m_listPortals.begin(); // DON'T use lpGetFirstPortal because of shared iterator !
while(itPortal != m_listPortals.end())
{
CGraphPortal* lpPortal = *itPortal;
CGraphPortal* lpGoal = lpPortal->lpGetGoal();
if(!lpGoal)
{
if(lpPortal->dwGetType() == CGraphPortal::_MIRROR_)
lpGoal = lpPortal;
else
{
itPortal++;
continue;
}
}
CGraphNode * lpNode = lpGoal ->lpGetParentNode();
CBVbase * lpBVPortal = lpPortal->lpGetBoundingVol();
// set portal->world trf
CMat4x4 m4Portal2World;
if(lpBVPortal && lpPortal->boGetWorldMatrix(&m4Portal2World))
lpBVPortal->vSetTrf2World(&m4Portal2World);
// transform camera position to portal repere
CVect3D v3CamWorldPos;
CVect3D v3CamPortalPos;
// portal in camera frustum ?
if(lpPortal->boIsEnabled() && lpCam->dwIsInFrustum(lpPortal))
{
// test against previous portals
// rem: planes are in camera space (to be node-independent)
CBVPlanes* lpPlanes = lpCam->lpGetBVPlanes();
if(lpBVPortal && (lpPlanes->hrIntersection(lpBVPortal) != CBVbase::_OUTSIDE_))
{
if(lpCam->boGetNodeWorldPos(&v3CamWorldPos) && lpPortal->boTransformToRepere(&v3CamPortalPos,v3CamWorldPos))
{
if(v3CamPortalPos[_Z_] > 0.f)
{ // portal facing the camera
// test : draw portal (mesh) to stencil
if(m_lpRenderer->boAllowStencil())
{
m_lpRenderer->vSetStencilWrite();
hrDraw(lpCamera);
m_lpRenderer->vSetStencilTest();
}
// push planes
DWORD dwPlaneNum[4];
if(lpBVPortal && m_boPortalCulling)
{
CBVAABB* lpPortalAABB = reinterpret_cast<CBVAABB*>(lpBVPortal);
CVect3D v3Min,v3Max;
lpPortalAABB->vGet(&v3Min,&v3Max);
CVect3D v3Corner[4]; // build corners (portal seen front => X to the left)
v3Corner[0].vSet(v3Max[_X_],v3Min[_Y_],0.f);
v3Corner[1].vSet(v3Max[_X_],v3Max[_Y_],0.f);
v3Corner[2].vSet(v3Min[_X_],v3Max[_Y_],0.f);
v3Corner[3].vSet(v3Min[_X_],v3Min[_Y_],0.f);
// CMat4x4 m4World2Cam;
// lpCam->boGetNodeWorldMatrixI(&m4World2Cam);
// CMat4x4 m4Portal2Cam = m4World2Cam*m4Portal2World;
CMat4x4 m4Portal2Cam = (*lpCam->lpm4Get2CamMatrix()) * m4Portal2World;
CVect3D v3Proj[5]; // trf to camera space
v3Proj[0].vSet(m4Portal2Cam*v3Corner[0],_W_);
v3Proj[1].vSet(m4Portal2Cam*v3Corner[1],_W_);
v3Proj[2].vSet(m4Portal2Cam*v3Corner[2],_W_);
v3Proj[3].vSet(m4Portal2Cam*v3Corner[3],_W_);
v3Proj[4].vSet(v3Proj[0]);
// build plane eq
for(DWORD dwPlane = 0; dwPlane < 4; dwPlane++)
{
CVect3D v3_01(v3Proj[dwPlane+1]-v3Proj[dwPlane]);
CVect3D v3N (v3Proj[dwPlane] ^v3_01);
if(lpCam->boIsLeftHanded()) v3N = -v3N;
CVect4D v4Eq (v3N,0.f);
dwPlaneNum[dwPlane] = lpPlanes->dwAddPlane(v4Eq);
}
}
// portal changes camera parameters
if(lpPortal->boUseSpecial())
{
// trf to portal space
CMat4x4 m4I;
lpPortal->boGetWorldMatrixI(&m4I);
m4I[_X_][_W_] = m4I[_Y_][_W_] = m4I[_Z_][_W_] = 0.f;
CVect3D v3CamWorldDir,v3CamWorldUp;
lpCam->boGetNodeWorldDir(&v3CamWorldDir);
lpCam->boGetNodeWorldUp (&v3CamWorldUp);
CVect3D v3CamPortalDir(m4I*v3CamWorldDir,_W_);
CVect3D v3CamPortalUp (m4I*v3CamWorldUp ,_W_);
// apply special trf (no translation)
CMat4x4* lpm4Trf = lpPortal->lpm4GetSpecialTrf();
v3CamPortalDir.vSet((*lpm4Trf)*v3CamPortalDir,_W_);
v3CamPortalUp .vSet((*lpm4Trf)*v3CamPortalUp ,_W_);
v3CamPortalPos.vSet((*lpm4Trf)*v3CamPortalPos,_W_);
// back to world space
CMat4x4 m4M;
lpPortal->boGetWorldMatrix(&m4M);
m4M[_X_][_W_] = m4M[_Y_][_W_] = m4M[_Z_][_W_] = 0.f;
v3CamWorldDir.vSet(m4M*v3CamPortalDir,_W_);
v3CamWorldUp .vSet(m4M*v3CamPortalUp ,_W_);
lpPortal->boTransformToWorld(&v3CamWorldPos,v3CamPortalPos);
// new camera
CGraphNode nodNew;
CCamera camNew(*lpCam);
nodNew.vSetAutoDelete(false); // local vars can't delete themselves
camNew.vSetAutoDelete(false);
nodNew.boAttachObject (&camNew);
nodNew.vSetPosition (v3CamWorldPos);
nodNew.vSetOrientation(v3CamWorldDir,v3CamWorldUp);
camNew.vSetLeftHanded(!lpCam->boIsLeftHanded());
camNew.boCalcTrfMatrix();
// copy BV planes
DWORD dwPlanes = lpPlanes->dwGetNbPlanes();
CBVPlanes* lpNewPlanes = camNew.lpGetBVPlanes();
// CMat4x4 m4Cam2World;
// camNew.boGetNodeWorldMatrix(&m4Cam2World);
// lpNewPlanes->vSetTrf2World (&m4Cam2World);
lpNewPlanes->vSetTrf2World(camNew.lpm4Get2WorldMatrix());
for(DWORD dwPlane = 0; dwPlane < dwPlanes; dwPlane++)
{
lpNewPlanes->dwAddPlane(lpPlanes->v4GetPlane(dwPlane,NULL));
}
// recurse
m_lpRenderer->boBegin3D(&camNew); // new cam->world trf
m_lpRenderer->vSetCullMode(camNew.boIsLeftHanded());
lpNode->vTraversal(&camNew,dwFrame,lpdwNodes);
m_lpRenderer->vSetCullMode(!camNew.boIsLeftHanded());
m_lpRenderer->boBegin3D(lpCam); // restore
nodNew.boDetachObject(&camNew); // don't forget that, the camera could be destroyed before the node
}
// same camera
else
{
lpNode->vTraversal(lpCamera,dwFrame,lpdwNodes);
}
// pop planes
if(lpBVPortal && m_boPortalCulling)
{
lpPlanes->boRemovePlane(dwPlaneNum[0]);
lpPlanes->boRemovePlane(dwPlaneNum[1]);
lpPlanes->boRemovePlane(dwPlaneNum[2]);
lpPlanes->boRemovePlane(dwPlaneNum[3]);
}
// disable stencil
if(m_lpRenderer->boAllowStencil())
{
m_lpRenderer->vSetStencilOff();
}
}
}
}
}
itPortal++;
}
// traverse node
hrDraw(lpCamera);
// traverse children
listNodeIterator itChild = m_listChildren.begin(); // DON'T use lpGetFirstChild because of shared iterator !
while(itChild != m_listChildren.end())
{
CGraphNode* lpChild = *itChild;
CBVPlanes* lpPlanes = lpCam ->lpGetBVPlanes();
CBVbase* lpBVNode = lpChild->lpGetBoundingVol();
CMat4x4 m4Node2World;
if(lpBVNode)
{
if(lpChild->boGetWorldMatrix(&m4Node2World))
lpBVNode->vSetTrf2World(&m4Node2World);
}
//!!! if(!lpBVNode || (lpPlanes->hrIntersection(lpBVNode) != CBVbase::_OUTSIDE_))
{
lpChild->vTraversal(lpCamera,dwFrame,lpdwNodes);
}
itChild++;
}
// traverse visitors
setNodeIterator itVisitor = m_setVisitors.begin();
while(itVisitor != m_setVisitors.end())
{
CGraphNode* lpVisitor = *itVisitor;
CBVbase* lpBVNode = lpVisitor->lpGetBoundingVol();
CMat4x4 m4Node2World;
if(lpBVNode)
{
if(lpVisitor->boGetWorldMatrix(&m4Node2World))
lpBVNode->vSetTrf2World(&m4Node2World);
}
lpVisitor->vTraversal(lpCamera,dwFrame,lpdwNodes);
itVisitor++;
}
}
/**********************************************************************************************************************/
/* OVERRIDABLES */
/**********************************************************************************************************************/
/************************************************ hrDraw **************************************************************/
// draw all objects in node
// 28/04/00, M - 05/10/00 visibility test
// in : current camera
// out: _OK_ or error ID
/**********************************************************************************************************************/
HRESULT CGraphNode::hrDraw(const CCamera* lpCamera)
{
if(!lpCamera) return _ERR_(_GLOBERR_BADPARAM_);
CCamera* lpCam = const_cast<CCamera*>(lpCamera);
CBVPlanes* lpPlanes = lpCam->lpGetBVPlanes();
// objects
listObjIterator itObj = m_listObjects.begin();
if(itObj != m_listObjects.end())
{
vUpdateBaseLights(); // update node's list of lights
m_lpRenderer->boUpdateLights(this); // enable/disable renderer lights
}
while(itObj != m_listObjects.end())
{
CGraphObj* lpObj = *itObj;
bool boDraw = true;
bool boAddV = false;
if(lpObj->boIsVisitor())
{
if(lpCam->boAlreadyDrawn(lpObj)) boDraw = false;
else boAddV = true;
}
boDraw &= lpObj->boIsVisible();
if(boDraw)
{
CMat4x4 m4Obj2World;
CBVbase* lpBV = lpObj->lpGetBoundingVol();
if(lpBV && boGetWorldMatrix(&m4Obj2World))
lpBV->vSetTrf2World(&m4Obj2World);
if(lpCam->dwIsInFrustum(lpObj))
{
lpObj->vSetCurrentCamera(lpCamera);
if(!lpBV || (lpPlanes->hrIntersection(lpBV) != CBVbase::_OUTSIDE_))
{
lpObj->hrDraw();
if(boAddV) lpCam->boAddVisitor(lpObj); // only when it's really drawn !
}
if(lpBV) lpBV->hrDraw(this);
}
}
itObj++;
}
// node's BV
CBVbase* lpBV = lpGetBoundingVol();
if(lpBV) lpBV->hrDraw(this);
return _ERR_(_OK_);
}
|