A very simple exampleThis is a 2 room scene. The BSP Tree has 3 nodes (2 of them are leaves). Red triangles are those we are using to clip against portals.
Step 1GeneratePortal(BspNode0,NULL) We generate a SuperPlane at the dividing plane of the root node of the bsp tree, and we also create a large portal (portal 1) on the SuperPlane(the blue rectangle). Assume the portal's plane normal points to BspNode0(the left space), and portal1.m_iLeftNode = -1;
Step 2GeneratePortal(BspNode1,SuperPlane) This is a leaf node, so we have to use all the triangles in BspNode1 to clip against all the portals in SuperPlane. We find the first suitable triangle (the red one), and divide portal 1 into portal 1 and portal2. Portal 1 is on the positive side of the triangle, so its m_bCulled member is set to FALSE (and it is shown in yellow). Portal 2 is on the negative side of the triangle, so its m_bCulled member is set to TRUE (and it is shown in blue). Step3We find the second suitable triangle, and divide portal 1 into portal 1 and portal 3. Portal 3 is the yellow one and portal 1 is the blue one at the top.
Step 4Next we find the third suitable triangle, and divide portal 3 into portal 3 and portal 4. Step5Next we find the last suitable triangle in BspNode1, and clip portal 4 into portal 4 and portal 5. Note that these pictures are from an old version, where we're doing all portal filter processing in the final step, but actually you can do some filter processing as we describe in the algorithm)
Step 6GeneratePortal(BspNode2,SuperPlane) BspNode2 is also a leaf node, so we have to use all triangles in the leaf to clip against all the portals in SuperPlane We find the first suitable trianle in BspNode2, and divide portal 6 into portal 5 and portal 6. Step 7We find the second suitable triangle, and clip portal 6 into portal 6 and portal 7. Step 8We find the last suitable triangle in BspNode2, and clip portal 7 into portal 7 and portal 8. Step 9After deleting all the blue portals we get what we want, the yellow portal that connects BspNode1 and BspNode2. Here is the detailed version: void CBspTree::GeneratePortal(CBspTreeNode *pBspNode_, CSuperPlane *pSuperPlane_) { CPortal *pTemp1,*pTemp2; int iCount; if (pBspNode_->IsLeaf ()) { if (!pSuperPlane_) return; //You should give us a bsp tree with more than 1 node! for (each triangle T in pBspNode_) { iCount = pSuperPlane_->m_ListOfPortals.GetCount(); for (each portal P in pSuperPlane_->m_ListOfPortals) { iCount--; if (iCount<0) break; //please keep in mind,this process is //to trace the validation of each portal int *pInt; if (P->m_iLeftNode == pBspNode->m_iTag) pInt = &P->m_iLeftNode; else if (P->m_iRightNode == pBspNode->m_iTag) pInt = &P->m_iRightNode; else continue; // P has no relationship with this pBspNode_,so just ignore P if (TriangleIntersectPolygon(T,P->m_polygon)) { pTemp1 = new CPortal; pTemp2 = new CPortal; pTemp1.m_bCulled = FALSE; pTemp2.m_bCulled = TRUE; ClipPortal(T->m_Plane, P,*pTemp1,*pTemp2); if (!pTemp1->Reasonable()|| !pTemp2->Reasonable()) { //Abort this operation delete pTemp1; delete pTemp2; continue; } pSuperPlane_->m_ListOfPortals.Add(pTemp1); pSuperPlane_->m_ListOfPortals.Add(pTemp2); pSuperPlane_->m_ListOfPortals.Remove(P); } } } //OK,now we can delete those Culled portals in this leaf! for (each portal P in pSuperPlane_->m_ListOfPortals) { if ((P->m_iLeftNode!=pBspNode_->m_iTag)&& (P->m_iRightNode!=pBspNode_->m_iTag)) continue; if (P->m_bCulled) pSuperPlane_->Remove(P); } } else { if (pSuperPlane) { iCount = pSuperPlane_->m_ListOfPortals.GetCount(); for (each portal P in pSuperPlane_->m_ListOfPortals) { iCount--; if (iCount<0) break; //please keep in mind,this process is //to trace connection information of each portal int *pInt; if (P->m_iLeftNode == pBspNode->m_iTag) pInt = &P->m_iLeftNode; else if (P->m_iRightNode == pBspNode->m_iTag) pInt = &P->m_iRightNode; else continue;// P has no relationship with this pBspNode_,so just ignore P switch (CalculateSide(pBspNode_->m_DividingPlane,&P->m_Polygon)) { case Polygon is on positive side of the dividing plane: *pInt = pBspNode_->m_pLeftNode->m_iTag; break; case Polygon is on negative side of the dividing plane: *pInt = pBspNode_->m_pRightNode->m_iTag; break; case Polygon is spanning the dividing plane: pTemp1 = new CPortal; pTemp2 = new CPortal; pTemp1->m_bCulled = P->m_bCulled; pTemp2->m_bCulled = P->m_bCulled; if (*pInt == pPortal->m_iLeftNode) { This means we only need to update the left connection information: pTemp1->m_iLeftNode = pBspNode_->m_pLeftNode->m_iTag; pTemp2->m_iLeftNode = pBspNode_->m_pRightNode->m_iTag; pTemp1->m_iRightNode = pBspNode_->m_pRightNode->m_iTag; pTemp2->m_iRightNode = pBspNode_->m_pRightNode->m_iTag; } else { This means we only need to update the right connection information: pTemp1->m_iRightNode = pBspNode_->m_pLeftNode->m_iTag; pTemp2->m_iRightNode = pBspNode_->m_pRightNode->m_iTag; pTemp1->m_iLeftNode = pBspNode_->m_pLeftNode->m_iTag; pTemp2->m_iLeftNode = pBspNode_->m_pLeftNode->m_iTag; } ClipPortal(pBspNode_->m_DividingPlane,P,*pTemp1,*pTemp2); pSuperPlane_->m_ListOfPortals.Add(pTemp1); pSuperPlane_->m_ListOfPortals.Add(pTemp2); pSuperPlane_->m_ListOfPortals.Remove(P); break; case Polygon is coincide with the dividing plane: This situation should not happen, but it could. If this happens, that means your bsp tree is not organized well, for you have used a dividing plane which almost coincide with another dividing plane on the parent node. You should make sure your bsp tree is reasonable, or just do nothing here. break; } } GeneratePortal(pBspNode_->m_pLeftNode,pSuperPlane_); GeneratePortal(pBspNode_->m_pRightNode,pSuperPlane_); } if (!pBspNode_->m_bPushed) //we haven't generated a SuperPlane at pBspNode_ { pBspNode_->m_bPushed = TRUE; CPortal* pPortal = new CPortal; CSuperPlane* pNewSuperPlane = new CSuperPlane; m_ListOfSuperPlanes.Add(pNewSuperPlane); On this superplane, generate a large enough polygon for the portal which exceeds the boundary of the bounding box of the environment. In our method we use a rectangle, but you can use other shapes as long as you meet the requirement. Then add those points to pPortal->m_Polygon In our assumption, the portals on the SuperPlane of pBspNode_ should only connect the regions in the subtree of pBspNode_, so we should confine the polygon of portal on the Superplane to the convex space of pBspNode_, this will optimize the performance. We achieve this by cliping the polygon against all the planes that on parent nodes of pBspNode_ as below: CbspNode* pTempNode,pPrevNode PPrevNode = pBspNode_; for (pTempNode = pBspNode_->m_pParent;pTempNode!= NULL; pTempNode = pTempNode->m_pParent) { if (pTempPortal is spanning the dividing plane of pTempNode) { ClipPortal(pTempNode->m_DividingPlane,pPortal,*pTemp1,*pTemp2); if (pPrevNode == pTempNode->m_pLeftNode) *pPortal = *pTemp1; else *pPortal = *pTemp2; delete pTemp1; delete pTemp2; } pPrevNode = pTempNode; } pNewSuperPlane->m_ListOfPortals.Add(pPortal); Now get the connection information of the Portal we have just generated, C3DPoint v1,v2,v3; v1=pPortal->m_Polygon.m_pPoints[0]-pPortal->m_Polygon.m_pPoints[1]; v2=pPortal->m_Polygon.m_pPoints[2]-pPortal->m_Polygon.m_pPoints[1]; v1.Normalize(); v2.Normalize(); v3=CrossProduct(v2,v1); if (DotProduct(v3, pBspNode->m_DividingPlane.Normal)>0) { The normal of the portal plane is the same as the normal of the dividing plane of the pBspNode_ pPortal->m_iLeftNode = pBspNode_->m_pLeftNode->m_iTag; pPortal->m_iRightNode = pBspNode_->m_pRightNode->m_iTag; } else { The normal of the portal plane is the opposite against the normal of the dividing plane of the pBspNode_ pPortal->m_iLeftNode = pBspNode_->m_pRightNode->m_iTag; pPortal->m_iRightNode = pBspNode_->m_pLeftNode->m_iTag; } Now the left thing is to push this superplane down to the subtree: GeneratePortal(pBspNode_->m_pLeftNode,pNewSuperPlane); GeneratePortal(pBspNode_->m_pRightNode,pNewSuperPlane); } } } |
|