PORTAL* ClipPortal ( long Node, PORTAL *Portal ) { // this recursive function repeatedly clips the current portal to the // tree until it ends up in a leaf at which point it is returned PORTAL* PortalList = NULL, *FrontPortalList = NULL; PORTAL* BackPortalList = NULL, *Iterator = NULL; PORTAL* FrontSplit = NULL, *BackSplit = NULL; switch ( ClassifyPoly ( &PlaneArray [ NodeArray [ Node ].Plane ], ( POLYGON* ) Portal ) ) { case CP_ONPLANE: { // the portal has to be sent down both sides of the tree and tracked, send // it down front first but do no delete any bits that end up in solid space, // just ignore them if ( NodeArray [ Node ].IsLeaf != 0 ) { // this is a leaf node ( the front is a leaf ) Portal->LeafOwnerArray [ Portal->NumberOfLeafs ] = NodeArray [ Node ].Front; Portal->NumberOfLeafs++; Portal->Next = NULL; Portal->Prev = NULL; FrontPortalList = Portal; } else { // send the portal down the front list and get returned a // list of PortalFragments that survived the front tree FrontPortalList = ClipPortal ( NodeArray [ Node ].Front, Portal ); } // then send each fragment down the back tree. if ( FrontPortalList == NULL ) return NULL;; if ( NodeArray [ Node ].Back == -1 ) return FrontPortalList; // loop through each front list fragment and send it down the back tree while ( FrontPortalList != NULL ) { PORTAL* tempnext = FrontPortalList->Next; BackPortalList = NULL; BackPortalList = ClipPortal ( NodeArray [ Node ].Back, FrontPortalList ); // now set to the end of the returned back fragment list to get the last fragment if ( BackPortalList != NULL ) { Iterator = BackPortalList; while ( Iterator->Next != NULL ) { Iterator = Iterator->Next; } // attach the last fragment to the first fragment from a previous loop Iterator->Next = PortalList; if ( PortalList != NULL ) { PortalList->Prev = Iterator; } // portal list now points at the current complete list of fragment collected from each loop PortalList = BackPortalList; } FrontPortalList = tempnext; } return PortalList; } break; case CP_FRONT: { // either send it down the front tree or add it to the portal // list because it has come out in empty space if ( NodeArray [ Node ].IsLeaf == 0 ) { PortalList = ClipPortal ( NodeArray [ Node ].Front, Portal ); return PortalList; } else { // the front node is an empty leaf so add the fragment to the portal list Portal->LeafOwnerArray [ Portal->NumberOfLeafs ] = NodeArray [ Node ].Front; Portal->NumberOfLeafs++; Portal->Next = NULL; Portal->Prev = NULL; return Portal; } } break; case CP_BACK: { // either send it down the back tree or delete it if no back tree exists // because it means it has come out in solid space if ( NodeArray [ Node ].Back != -1 ) { PortalList = ClipPortal ( NodeArray [ Node ].Back, Portal ); return PortalList; } else { DeletePortal ( Portal ); Portal = NULL; return NULL; } } break; case CP_SPANNING: { // portal fragment is spanning the plane, so it must be split FrontSplit = new PORTAL; BackSplit = new PORTAL; ZeroMemory ( FrontSplit, sizeof ( PORTAL ) ); ZeroMemory ( BackSplit, sizeof ( PORTAL ) ); // split the portal fragment into two SplitPortal ( Portal, &PlaneArray [ NodeArray [ Node ].Plane ], FrontSplit, BackSplit ); // delete the original portal fragment DeletePortal ( Portal ); Portal = NULL; // there is another front node if ( NodeArray [ Node ].IsLeaf == 0 ) { FrontPortalList = ClipPortal ( NodeArray [ Node ].Front, FrontSplit ); } else { // it's about to get pushed into a leaf FrontSplit->LeafOwnerArray [ FrontSplit->NumberOfLeafs ] = NodeArray [ Node ].Front; FrontSplit->NumberOfLeafs++; FrontSplit->Prev = NULL; FrontSplit->Next = NULL; FrontPortalList = FrontSplit; } // the backsplit is in solid space if ( NodeArray [ Node ].Back != -1 ) { BackPortalList = ClipPortal ( NodeArray [ Node ].Back, BackSplit ); } else { // we ended up in solid space DeletePortal ( BackSplit ); BackSplit = NULL; } // find the end of the list and attach it to front back list if ( FrontPortalList != NULL ) { // there is something in the front list Iterator = FrontPortalList; while ( Iterator->Next != NULL ) { Iterator = Iterator->Next; } if ( BackPortalList != NULL ) { Iterator->Next = BackPortalList; BackPortalList->Prev = Iterator; } return FrontPortalList; } else { // there is nothing in the front list if ( BackPortalList != NULL ) return BackPortalList; return NULL; } // if we got here, we are fresh out of portal fragments // so simply return NULL return NULL; } break; default: return NULL; break; } return NULL; }
Poly *Poly::ClipToList ( Poly *pPoly_, bool bClipOnPlane_ ) { switch ( ClassifyPoly ( pPoly_ ) ) { case eCP::FRONT: { return pPoly_->CopyPoly ( ); } break; case eCP::BACK: { if ( IsLast ( ) ) { return NULL; } return m_pNext->ClipToList ( pPoly_, bClipOnPlane_ ); } break; case eCP::ONPLANE: { double Angle = plane.n.Dot ( pPoly_->plane.n ) - 1; if ( ( Angle < epsilon ) && ( Angle > -epsilon ) ) { if ( !bClipOnPlane_ ) { return pPoly_->CopyPoly ( ); } } if ( IsLast ( ) ) { return NULL; } return m_pNext->ClipToList ( pPoly_, bClipOnPlane_ ); } break; case eCP::SPLIT: { Poly *pFront = NULL; Poly *pBack = NULL; SplitPoly ( pPoly_, &pFront, &pBack ); if ( IsLast ( ) ) { delete pBack; return pFront; } Poly *pBackFrags = m_pNext->ClipToList ( pBack, bClipOnPlane_ ); if ( pBackFrags == NULL ) { delete pBack; return pFront; } if ( *pBackFrags == *pBack ) { delete pFront; delete pBack; delete pBackFrags; return pPoly_->CopyPoly ( ); } delete pBack; pFront->AddPoly ( pBackFrags ); return pFront; } break; } return NULL; }
//determines which polygon would be the best to split. Returns the integer index of that //polygon. Note that it assumes that the list contain at least one element. In addition, //it will return the number of polygons that lie on the same plane to make allocation //of nodes easier, along with how many will end up on the front and back static uint32 FindBestSplitPlane(PrePolyArray& PolyList, uint32& nNumLieOn, uint32& nNumFront, uint32& nNumBack) { ASSERT(PolyList.GetSize() > 0); uint32 nBestIndex = 0; uint32 nBestScore = 0xFFFFFFFF; //plane information PVector vNormal; PReal fPlaneDist; //counts uint32 nFront, nBack, nSplit, nOn; //the poly classification uint32 nType; //the number of polygons to test uint32 nNumPolies = PolyList.GetSize(); //the amount of polygons to skip over. Currently just do 2 * sqrt(n) //samples, so 20 polygons will be sampled for 100 nodes, etx. This //speeds up BSP generation time significantly. uint32 nPolyInc = LTMAX(1, (uint32)(nNumPolies / sqrt(4.0 * nNumPolies))); for(uint32 nCurrPoly = 0; nCurrPoly < nNumPolies; nCurrPoly += nPolyInc) { fPlaneDist = PolyList[nCurrPoly]->Dist(); vNormal = PolyList[nCurrPoly]->Normal(); //reset the info nFront = nBack = nSplit = nOn = 0; //now need to count up the split information for(uint32 nTestPoly = 0; nTestPoly < PolyList.GetSize(); nTestPoly++) { //skip over the current poly if(nTestPoly == nCurrPoly) { nOn++; continue; } nType = ClassifyPoly(vNormal, fPlaneDist, PolyList[nTestPoly]); if(nType == PLANE_SPAN) nSplit++; else if(nType == PLANE_FRONT) nFront++; else if(nType == PLANE_BACK) nBack++; else nOn++; } //ok, figure out the score uint32 nScore = CalcSplitScore(nSplit, nFront, nBack); if(nScore < nBestScore) { nBestScore = nScore; nBestIndex = nCurrPoly; nNumLieOn = nOn; nNumFront = nFront + nSplit; nNumBack = nBack + nSplit; } } return nBestIndex; }
static bool RecursivelyBuildBSP(CLightBSPNode** ppNode, PrePolyArray& PolyList, CLightBSPPoly** ppBSPPolyList) { //sanity check ASSERT(ppNode); ASSERT(PolyList.GetSize() > 0); //number of polies that lie on the plane uint32 nNumLieOn, nFront, nBack; //first off, find the best splitting plane uint32 nSplitPlane = FindBestSplitPlane(PolyList, nNumLieOn, nFront, nBack); //allocate the new node *ppNode = AllocateBSPNode(nNumLieOn); //check for memory failure if(*ppNode == NULL) return false; //setup the node (*ppNode)->m_vPlaneNorm = PolyList[nSplitPlane]->Normal(); (*ppNode)->m_fPlaneDist = PolyList[nSplitPlane]->Dist(); //create the front, back, and on arrays PrePolyArray Front, Back, On; Front.SetSize(nFront); Back.SetSize(nBack); On.SetSize(nNumLieOn); //now fill up all the lists PVector vNormal = PolyList[nSplitPlane]->Normal(); float fDist = PolyList[nSplitPlane]->Dist(); //index to the coplanar poly offset uint32 nPolyIndex = 0; //indices for the lists uint32 nFrontIndex = 0; uint32 nBackIndex = 0; uint32 nOnIndex = 0; uint32 nType; //now need to count up the split information for(uint32 nTestPoly = 0; nTestPoly < PolyList.GetSize(); nTestPoly++) { if(nTestPoly == nSplitPlane) { On[nOnIndex++] = PolyList[nTestPoly]; (*ppNode)->m_pPolyList[nPolyIndex++] = ppBSPPolyList[PolyList[nTestPoly]->m_Index]; continue; } nType = ClassifyPoly(vNormal, fDist, PolyList[nTestPoly]); if(nType & PLANE_FRONT) Front[nFrontIndex++] = PolyList[nTestPoly]; if(nType & PLANE_BACK) Back[nBackIndex++] = PolyList[nTestPoly]; if(nType == PLANE_ON) { On[nOnIndex++] = PolyList[nTestPoly]; (*ppNode)->m_pPolyList[nPolyIndex++] = ppBSPPolyList[PolyList[nTestPoly]->m_Index]; } } //sanity check ASSERT(nPolyIndex == (*ppNode)->m_nNumPolies); ASSERT(nFrontIndex == nFront); ASSERT(nBackIndex == nBack); ASSERT(nOnIndex == nNumLieOn); //build up the child lists bool bSuccess = true; if(Front.GetSize() > 0) { bSuccess = RecursivelyBuildBSP(&(*ppNode)->m_pFront, Front, ppBSPPolyList); } if(bSuccess && (Back.GetSize() > 0)) { bSuccess = RecursivelyBuildBSP(&(*ppNode)->m_pBack, Back, ppBSPPolyList); } //see if we need to clean up if(!bSuccess) { FreeBSPNode(*ppNode); *ppNode = NULL; return false; } //we need to update the node's bounding sphere. This is done by running through all //the polygons on the plane and generating a bounding box, finding its center, and then //the maximum distance to the points PVector vMin((PReal)MAX_CREAL, (PReal)MAX_CREAL, (PReal)MAX_CREAL); PVector vMax(-vMin); uint32 nCurrPoly; for(nCurrPoly = 0; nCurrPoly < nNumLieOn; nCurrPoly++) { CPrePoly* pPoly = On[nCurrPoly]; for(uint32 nCurrVert = 0; nCurrVert < pPoly->NumVerts(); nCurrVert++) { VEC_MIN(vMin, vMin, pPoly->Pt(nCurrVert)); VEC_MAX(vMax, vMax, pPoly->Pt(nCurrVert)); } } //found the center (*ppNode)->m_vBSphereCenter = ((vMin + vMax) / 2); //now update the radius (*ppNode)->m_fBSphereRadSqr = 0; PReal fDistSqr; for(nCurrPoly = 0; nCurrPoly < nNumLieOn; nCurrPoly++) { CPrePoly* pPoly = On[nCurrPoly]; for(uint32 nCurrVert = 0; nCurrVert < pPoly->NumVerts(); nCurrVert++) { fDistSqr = (pPoly->Pt(nCurrVert) - (*ppNode)->m_vBSphereCenter).MagSqr(); if(fDistSqr > (*ppNode)->m_fBSphereRadSqr) { (*ppNode)->m_fBSphereRadSqr = fDistSqr; } } } //add some padding to the radius to compensate for inaccuracy (*ppNode)->m_fBSphereRadSqr += NODE_RADIUS_PADDING; return bSuccess; }
static void BSPTreeCreateRecursive(BSPTreeNode *tree, PolyListNode *pllist, #if BSPTREE_STATS int depth, #endif struct obstack *scratch) { PolyListNode *plnode, *front, *back; EdgeIntersection edges[2]; tree->front = tree->back = NULL; ListPop(pllist, plnode); tree->polylist = plnode; check_poly(plnode->poly); PolyPlane(plnode, &tree->plane); front = back = NULL; while (pllist) { ListPop(pllist, plnode); check_poly(plnode->poly); switch (ClassifyPoly(&tree->plane, plnode->poly, edges)) { case BACKOF: check_poly(plnode->poly); ListPush(back, plnode); break; case COPLANAR: check_poly(plnode->poly); ListPush(tree->polylist, plnode); break; case INFRONTOF: check_poly(plnode->poly); ListPush(front, plnode); break; case BOTH_SIDES: check_poly(plnode->poly); SplitPolyNode(plnode, &front, &back, edges, scratch); break; } } if (front) { tree->front = obstack_alloc(scratch, sizeof(*tree->front)); BSPTreeCreateRecursive(tree->front, front, #if BSPTREE_STATS depth+1, #endif scratch); } if (back) { tree->back = obstack_alloc(scratch, sizeof(*tree->back)); BSPTreeCreateRecursive(tree->back, back, #if BSPTREE_STATS depth+1, #endif scratch); } #if BSPTREE_STATS if (depth > tree_depth) { tree_depth = depth; } #endif }