二进制空间分区 - 空间分区逻辑中的错误

时间:2013-10-04 04:55:47

标签: c++ c++11 3d bsp-tree

所以我一直在实现我的第一个BSP树,我想我的逻辑中发现了一个缺陷。我很遗憾我如何能够以适当的方式正确地重构它。

这是构造函数(解释如下):

BSPTree::BSPTree( const polygonList_t& list )
    : mRoot( nullptr )
{
    polygonList_t front, back;

    auto eol = list.end();
    Polygon* rootSplitter = list[ 0 ];

    vec3 rootSplitPos;
    rootSplitter->GetPosition( rootSplitPos );

    for( auto iPoly = list.begin() + 1; iPoly != eol; ++iPoly )
    {
        vec3 resolvePos;
        ( *iPoly )->GetPosition( resolvePos );

        switch( ResolvePosition( rootSplitPos, resolvePos ) )
        {
            case POSITION_FRONT:
                front.push_back( *iPoly );
                break;

            case POSITION_BACK:
                back.push_back( *iPoly );
                break;

            case POSITION_SPLIT:
            {
                Polygon* toSplit = *iPoly;
                list.erase( iPoly );

                Polygon* outFront = new Polygon;
                Polygon* outBack  = new Polygon;

                SplitPolygon( resolvePos, toSplit, outFront, outBack );

                front.push_back( outFront );
                back.push_back( outBack );

                // finally we kill this one off
                delete toSplit;
                toSplit = nullptr;
            }
                break;
        }
    }

    mRoot = BSP_MakeNode();

    mRoot->splitter = rootSplitter;

    SplitSpace( mRoot, front );
    SplitSpace( mRoot, back );
}

简而言之,我们收到一个typedeffed std::vector< Polygon* >,其中包含任意数量的堆分配的Polygon对象。然后我们想把它们分成两类:在某个中心元素前面的那些和后面的那些元素。当然,我们声明了两个相同typedef的列表,并分别称它们为frontback

首先,我们选择一个Polygon(最终我想找到一个看起来最适合根分区平面的多边形),然后我们遍历原始列表,检查以下三种情况之一:< / p>

注意 - 为了简洁起见,我只是将我们的根分区多边形复制为 root

  • POSITION_FRONT:我们知道列表中的当前多边形位于 root 之前,因此我们自然会将此多边形添加到前面的列表中。

  • POSITION_BACK:与位置相同,唯一的区别是此多边形位于 root 之后。

  • POSITION_SPLIT:我们无法确定此多边形是否位于 root 之前或之后,因此我们将其拆分为两个并插入前面和将它的一部分放回各自的清单中。

最后,一旦我们将多边形分成前后列表,我们就会进一步细分我们的空间,使用根作为初始细分的基础。

void BSPTree::SplitSpace( bspNode_t* node, polygonList_t& polygons )
{
    if ( polygons.size() == 0 ) return;

    // grab the first polygon within the list,
    // and then subtract it from the list itself.
    Polygon* next = polygons[ 0 ];
    polygons.pop_back();

    vec3 splitPos;
    node->splitter->GetPosition( splitPos );

    vec3 toResolve;
    next->GetPosition( toResolve );

    switch( ResolvePosition( splitPos, toResolve ) )
    {
        case POSITION_FRONT:
            node->front = BSP_MakeNode();
            node->front->splitter = next;
            SplitSpace( node->front, polygons );
            break;

        case POSITION_BACK:
            node->back = BSP_MakeNode();
            node->back->splitter = next;
            SplitSpace( node->back, polygons );
            break;

        case POSITION_SPLIT:
        {
            Polygon* outFront = new Polygon;
            Polygon* outBack  = new Polygon;

            SplitPolygon( toResolve, next, outFront, outBack );

            node->front = BSP_MakeNode();
            node->back  = BSP_MakeNode();

            node->front->splitter = outFront;
            node->back->splitter = outBack;

            SplitSpace( node->front, polygons );
            SplitSpace( node->back, polygons );
        }
            break;
    }
}

现在,我们执行一个非常相似的操作序列,关键区别在于我们进一步细分已经分区的空间,直到每个多边形在节点树中具有给定位置,该位置在前面或后面其父节点。当然,我们递归地这样做。

我目前看到的问题在于上面的switch语句中的POSITION_SPLIT案例评估:

        case POSITION_SPLIT:
        {
            Polygon* outFront = new Polygon;
            Polygon* outBack  = new Polygon;

            SplitPolygon( toResolve, next, outFront, outBack );

            node->front = BSP_MakeNode();
            node->back  = BSP_MakeNode();

            node->front->splitter = outFront;
            node->back->splitter = outBack;

            SplitSpace( node->front, polygons ); // here
            SplitSpace( node->back, polygons );  // and here
        }

结合其他两个因素:

  • 对于从给定参考参数polygons获得的每个多边形,我们pop_back()指定在将该多边形分配给临时数据后将其保留在列表中的指针。

  • 使用上述内容,每次调用SplitSpace(...)都会产生一个检查,以查看其收到的列表是否为空。如果是,则不执行任何操作,并且已完成该列表的递归细分。

由于这两个因素,我不禁想到,在POSITION_SPLIT案例评估中,对SplitSpace(...)的第二次调用毫无用处:列表将在第二次调用之前耗尽(以适应分割的后退部分)。

问题

那么,这个问题的解决方案是什么,至少会让我回到正轨?

1 个答案:

答案 0 :(得分:1)

您应该将BSPTree构造函数重构为递归逻辑并应用“分而治之”。
1.输入是多边形列表 2.选择一个分裂平面,这是BSP中的当前节点。
3.将多边形细分为正面和背面 4.将前面的列表传递给同一个函数(递归),返回一个子节点 5.将后面的列表传递给同一个函数(递归),返回一个子节点 6.返回当前节点。

bspNode_t* BuildBSP( const polygonList_t& list )
{
 polygonList_t front, back;
 Polygon* rootSplitter = list[ 0 ];
 bspNode_t* currentNode = new bspNode_t(rootSplitter);

 vec3 rootSplitPos;
 rootSplitter->GetPosition( rootSplitPos );

 for( auto iPoly = list.begin() + 1; iPoly != eol; ++iPoly )
 {
   vec3 resolvePos;
   ( *iPoly )->GetPosition( resolvePos );

   switch( ResolvePosition( rootSplitPos, resolvePos ) )
   {
     case POSITION_FRONT:
       front.push_back( *iPoly );
       break;

     case POSITION_BACK:
       back.push_back( *iPoly );
       break;

     case POSITION_SPLIT:
     {
       Polygon* toSplit = *iPoly;
       list.erase( iPoly );

       Polygon* outFront = new Polygon;
       Polygon* outBack  = new Polygon;

       SplitPolygon( resolvePos, toSplit, outFront, outBack );

       front.push_back( outFront );
       back.push_back( outBack );

       // finally we kill this one off
       delete toSplit;
       toSplit = nullptr;

       break;
    }
  }
}

currentNode->frontChild = BuildBSP(front);
currentNode->backChild = BuildBSP(back);

return currentNode;

}