void NV_PureKdTreeRenderAction::TraverseNode(KdTreeNode *pCurrNode) { if (pCurrNode->m_bLeaf) { // this has a frame counter to prevent double rendering pCurrNode->RenderGeometry(); } else { Point3f ptViewpoint = GetState()->GetCamera()->GetPosition(); //---- insert the children in the reverse order as they shall be dealt with if (ptViewpoint[pCurrNode->m_iSplitAxis] > pCurrNode->m_fSplitValue) { m_TraverseStack.Push(pCurrNode->m_pLeftChild); m_TraverseStack.Push(pCurrNode->m_pRightChild); } else { m_TraverseStack.Push(pCurrNode->m_pRightChild); m_TraverseStack.Push(pCurrNode->m_pLeftChild); } } // no leaf } //---- the actual render algorithm void NV_PureKdTreeRenderAction::RenderSorted() { KdTreeNode * pCurrNode; int iTestCounter = 0; //---- clear the two needed datastructures m_TraverseStack.Clear(); m_NVTestQueue.Clear(); //---- generate the occlusion queries glGenOcclusionQueriesNV(m_nNumNodes, m_pOcclusionQueries); //---- take the root as a start m_pKdTree->m_iPlaneMask = 0x3f; // = 0011 1111 which means that at the beginning, all six planes have to frustum culled m_TraverseStack.Push(m_pKdTree); //---- the loop while ((!m_TraverseStack.Empty()) || (!m_NVTestQueue.Empty())) { bool result_available; while (!m_NVTestQueue.Empty() && (m_TraverseStack.Empty() || (result_available = ResultAvailable(m_NVTestQueue.GetFirst()->m_nTest)) ) ) { pCurrNode = m_NVTestQueue.GetFirst(); m_NVTestQueue.RemoveFirst(); // the next line is only interesting if we want to avoid double rendering already here: //bool was_visible = pCurrNode->m_bVisible && (m_nLastVisitedId == (GetCurrentFrame() - 1)); // update visibility classification (this is the wait variety!) pCurrNode->m_bVisible = GetVisiblePixels(pCurrNode->m_nTest) > Settings::Global()->get_nvocc_pixel_treshold(); pCurrNode->m_nLastVisitedId = GetCurrentFrame(); if (pCurrNode->m_bVisible) // && !was_visible) // note: could prevent double rendering already here! { pCurrNode->MakeParentsVisible(); TraverseNode(pCurrNode); } // visible } //---- the second part manages the distance heap if (!m_TraverseStack.Empty()) { //---- get the element with the lowest distance pCurrNode = m_TraverseStack.Pop(); // question: what is the difference if we do frustum culling only after setting visibility? if (pCurrNode->IsWithinViewFrustum()) { bool was_visible = pCurrNode->m_bVisible && (pCurrNode->m_nLastVisitedId == (GetCurrentFrame() - 1)); // note: this is actually only done for internal nodes and could be skipped for leaf nodes // in which case leaf node double rendering could be prevented here (see above) pCurrNode->m_bVisible = FALSE; pCurrNode->m_nLastVisitedId = GetCurrentFrame(); if (pCurrNode->m_bLeaf || !was_visible) { if (pCurrNode->m_pSuccessor->IssueNVTest(m_pOcclusionQueries[iTestCounter])) ++iTestCounter; m_NVTestQueue.Append(pCurrNode); } if (was_visible) { TraverseNode(pCurrNode); } } } } //---- tell the driver that the occlusion queries won't be needed any more glDeleteOcclusionQueriesNV(m_nNumNodes, m_pOcclusionQueries); }