portal自动生成

来源:互联网 发布:排版相册套版软件 编辑:程序博客网 时间:2024/06/06 18:30

 因为我负责游戏的场景管理部分,最近也在狂补场景管理方面的知识,主要是参考DOMM Q3和Renderware的代码,至于GAMEBRYO的场景管理实在不够使,就给了个纯粹的场景树,和半条命2和unreal我看最近我是没时间看了.

室外场景管理基本敲定了,跟FARCRY一样,我在网上看了HL2场景管理的方式,确实很特别,可惜我还没到哪个水准,也没是了,就用GEOMIPMAP吧,剩下要做的就是一些关键加速算法的实现,比如遮挡剔除,比较难搞.自动水面和海浪生成可以用现成的,估计要做简单的CSG编辑功能.

最近关注的主要是室内场景管理,参考了Q3MAP的代码,BSP PORTAL PVS,在Q3中BSP主要是用CSG生成的面所分割的,完成空间分割后再将BRUSH几何体分类到各个小空间中去,MESH 也是.主要是还是PORTAL自动生成的问题, Automatic Portal Generation比较容易懂,我先看了下这篇文章,还找了个中文的PDF,建议看英文,比较好懂, 我把读的过程写下来加深自己的印象.

文章中有个例子,第一次我没仔细看,搞得我后面很郁闷,我先把这个例子提一下,它假设已经有个棵BSP书,只有2级,root和2个叶接点 GeneratePortal(BspNode0,NULL) BspNode0为根接点,请注意了第2个参数SuperPlane 第一次传进去的是NULL,SuperPlane 就是用来保存Portal类的.这里BspNode0不是叶接点,而且Superplane也为NULL,算法中会为BspNode0生成一个superplane,如何生成这个superplane呢?这里有个关键的概念,因为这个superplane中的portal是连接BspNode0空间中的2个凸空间的,(BSP就是一次将空间分为2部分),所以这个portal的大小最大不能超过BspNode0空间的大小,固要用BspNode0的父接点中的分割平面来分割这个superplane.

 

pBspNode_->m_bPushed = TRUE;
         CPortal* pPortal = new CPortal;
         CSuperPlane* pNewSuperPlane = new CSuperPlane;
         m_ListOfSuperPlanes.Add(pNewSuperPlane);
 
         //***********************************************************

他省略了一些代码:在BspNode0分割面的位置上,生成一个足够大的矩形(也可以是其他形状),这个矩形必须延伸到整个BSP树空间之外,用BspNode0所有父接点上的分割面分割这个大矩形,为的是将这个大矩形限制在BspNode0子空间内,防止这个portal多边型干扰到其他子空间,明白?

pPortal中已经有个巨大的矩形了,并且已经加入到了pNewSuperPlane中,pNewSuperPlane又加到了m_ListOfSuperPlanes中

       //***************************************************************
         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)
            {

//*************************************************************************************

COME ON! 分析一下这下面的代码: 很显然用父接点中的空间分割平面分割BspNode0的superplane的巨大矩形,问题是之后那段if代码是干嘛的呢?判断当前接点是父接点的前面还是后面,如果是前面则分割后portal的后部分就可以扔掉了,也就是说这段代码是保留BspNode0的大矩形上属于本子空间的部分,很饶口....

//**************************************************************************************
               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);
//****************************************************************************

计算分割后的portal中多变形的发线,根据法线方向设置其连接的2个空间编号

//*******************************************************************************

 

第 2步 GeneratePortal(BspNode1,SuperPlane)

BspNode1(带几何信息的叶接点)  superplane就是第一步产生的切割portal

//用所有三角形依次切割superplane中所有portal的多边形

      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);
      }
//我觉得这部分很简单就是判断一个三角是否与portal中的多边形是否相交,然后用三角形所在的平面对多边形进行切割,需要主要的是这里的相交判断需要有一点点容差处理.后面我会讲到

我觉得最难理解的还是第3种情况GeneratePortal(BspNodeX,SuperPlane)

BspNodeX非叶接点, superplane从父接点传下来的.

if (pSuperPlane)
      {
         iCount = pSuperPlane_->m_ListOfPortals.GetCount();
         for (each portal  P  in pSuperPlane_->m_ListOfPortals)
         {
            iCount--;
            if (iCount<0) break;
//************************************************************

代码需要跟踪这个portal到底连接到了哪2个空间

//************************************************************
            int *pInt;//判断本接点在父接点传下来的portal的哪边?
            if (P->m_iLeftNode == pBspNode->m_iTag)
               pInt = &P->m_iLeftNode;//如果在前边,则只需要修改portal的前接点
            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
//用这个接点的分割平面测试父接点传下来的portal多边形的方位
            switch (CalculateSide(pBspNode_->m_DividingPlane,&P->m_Polygon))
            {

//很显然,如果这个portal多变形在接点分割面的前边,则这个portal连接到了这个接点的前子空间
            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:

//这个最麻烦,要切割这个portal
               pTemp1 = new CPortal;
               pTemp2 = new CPortal;
               pTemp1->m_bCulled = P->m_bCulled;
               pTemp2->m_bCulled = P->m_bCulled;

//将这2个新portal的左接点设置好,右保持和老portal 一样不变
               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:
               
//居然与父分割面共面,你BSP树形成的不合理呀,什么都不干
               break;
            }
         }
         GeneratePortal(pBspNode_->m_pLeftNode,pSuperPlane_);
         GeneratePortal(pBspNode_->m_pRightNode,pSuperPlane_);
      }

Q3的portal生成与这个差不多,而且没有做叶接点几何体切分,过2天把Q3代码分析温习温习,88

原创粉丝点击