查看: 9753|回复: 21

关于osg::ComputeBoundsVisitor求包围盒的问题(有描述\现象\重要代码\注释\思考)

[复制链接]

该用户从未签到

发表于 2010-12-22 16:22:40 | 显示全部楼层 |阅读模式
前沿:
    1、这个问题以前在一个帖子里也问过,不过最终并没有得到解决,为了突出问题的核心,我将不必要的东西简化了。我把问题、现象和我的思考贴在下面,大侠们给指点指点;
    2、发这个帖子也费了点力气,谢谢各位的真诚帮助。
    一、问题:利用osg::ComputeBoundsVisitor返回的包围盒究竟是整个全局世界坐标系(也就是root根节点的坐标系)下的包围盒,还是在待求节点的父节点坐标系下的包围盒?
    二、已做工作:
    1、array在已前的帖子中确定一定以及肯定地告诉我,肯定返回的是以root根节点坐标系为基准的包围盒,因为他一直这么用没有问题;
    2、array说,他一般不用PAT节点,而用MatrixTransform节点来设置节点的旋转、平移等操作;
    3、利用osg::ComputeBoundsVisitor求包围盒的代码如下(应该是对的吧。。)
           osg::ComputeBoundsVisitor boundvisitor;
           head->accept(boundvisitor);//head是读进来的一个节点
           osg::BoundingBox bb;
           bb = boundvisitor.getBoundingBox();
    三、代码及现象描述:
    1、我的场景结构非常简单,如图
scenegraph.jpg
    有些部分解释下:整个树的左支是主体,两个MT节点负责平移和放缩。树的右支是为了显示左边的head节点的包围盒用的,具体思路是这样,首先构造一个中心在(0,0,0)边长为1的box节点,然后根据head节点的包围盒bb中的相关信息去设置boxBounding节点的矩阵,这样的效果就是这个box的大小就是head包围盒的大小,位置也移到了head包围盒的中心位置,让他们同时在场景中显示。(为了显示清楚,我显示了head的线框模型,box的边线也加粗了)
    2、重要代码及注释
    //省略头文件   
void main(void)
{//读入一个head模型,它是一个红色线框显示的正方体
osg::ref_ptr<osg::Node> head = osgDB::readNodeFile("head.flt");

//下面使这个head节点线框显示
osg::ref_ptr<osg:olygonMode> polymode= new osg::PolygonMode;
polymode->setMode(osg::PolygonMode::FRONT_AND_BACK,osg::PolygonMode:INE);
head->getOrCreateStateSet()->setAttributeAndModes(polymode.get(),osg::StateAttribute::ON);
head->getOrCreateStateSet()->setMode(GL_CULL_FACE,osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);

//下面读入一个球,和head一样大小,用来示意位置
osg::ref_ptr<osg::Node> sphere = osgDB::readNodeFile("sphere.flt");

//下面定义一个mtAll节点,朝X正方向偏移,同时将那个sphere节点挂上去
osg::ref_ptr<osg::MatrixTransform> mtAll=new osg::MatrixTransform;
mtAll->setMatrix(osg::Matrix::translate(20,0,0));
mtAll->addChild(sphere.get());

//定义一个headTrans节点,再朝X正方向偏移,挂入head节点,同时将其本身挂到mtAll节点上去,树的左半边构建完毕
osg::ref_ptr<osg::MatrixTransform> headTrans=new osg::MatrixTransform;
headTrans->setMatrix(osg::Matrix::translate(20,0,0));//移动
headTrans->addChild(head.get());//将head节点挂上去
mtAll->addChild(headTrans.get());//将其本身挂到mtAll节点上去,树的左半边构建完毕

//计算head的包围盒
osg::ComputeBoundsVisitor boundvisitor;
headTrans->accept(boundvisitor);//用headTrans节点去接受这个访问器
osg::BoundingBox bb;
bb = boundvisitor.getBoundingBox();

//下面是构造场景树的右支,box是一个绿色粗线条显示的方体,代表包围盒
    osg::ref_ptr<osg::MatrixTransform> boxBounding=new osg::MatrixTransform;
    osg::ref_ptr<osg::Geode> box = new osg::Geode;
    osg::ref_ptr<osg::ShapeDrawable> sd = new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0f,0.0f,0.0f),1));
    sd->setColor(osg::Vec4(0.0,1.0,0.0,1.0));//设置颜色绿色,好区别
    osg::ref_ptr<osg::StateSet> state = sd->getOrCreateStateSet();
    osg::ref_ptr<osg::PolygonMode> pm = new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE);
    state->setAttributeAndModes(pm.get());//也设置线框模式
osg::ref_ptr<osg::LineWidth> lw =new osg::LineWidth(10.0);
state->setAttribute(lw.get());//设置线条粗细,看的清
box->addDrawable(sd.get());
boxBounding->addChild(box.get());

//下面就是将那个box按照head的包围盒来进行缩放和移动,以使得这个box刚好能表示包围盒所在位置
boxBounding->setMatrix(osg::Matrix::scale(bb.xMax()-bb.xMin(),bb.yMax()-bb.yMin(),bb.zMax()-bb.zMin())*osg::Matrix::translate(bb.center()));

osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
osg::ref_ptr<osg::Group> root = new osg::Group;     
root->addChild(mtAll.get());
root->addChild(boxBounding.get());
    viewer->setCameraManipulator(new osgGA::TrackballManipulator);//漫游
    viewer->setSceneData(root.get());
while (!viewer->done())
      {
     viewer->frame();
      }
}
    3、现象以及思考
    运行结果如图所示:
xiaoguo.jpg
    分析:
    (1)根据线框颜色很明显判别,左边的绿框是场景树的右枝,也就是说包围盒的位置和大小就应该在那儿;右边的红色的是head节点,确实平移了。
    (2)很明显,这个包围盒(绿色的部分)并没有正确地包住head节点(红色线条的方体)。
    思考:
    (1)在接受访问器的时候我是用的headTrans这个节点接受的,因为我仅仅想获得head节点的包围盒,而不需要head+sphere整个节点的包围盒;我如果用mtAll这个节点去接受该访问器,就获得了整个的包围盒,这不是我要的结果,效果如图: xiaoguo2.jpg
     (2)从上面的效果看,我感觉用 osg::ComputeBoundsVisitor计算出的包围盒仅仅是以其父节点坐标系为基准,而不是以根节点为基准的,否则的话这样的代码应该能得到我要的效果。
      (3)我如果仅仅想获得head节点的包围盒该怎么办呢?



/***************************************/
    写了这么多,无非是想把问题描述清楚,也是按照问题、做实验、上代码的要求写的,望各位老师能够抽一点时间帮忙看下,在此一并感谢~~~

该用户从未签到

 楼主| 发表于 2010-12-22 16:41:26 | 显示全部楼层
也许我帖子写的太长了,可是短了又描述不清楚。。。纠结。。。。

该用户从未签到

发表于 2010-12-22 21:35:29 | 显示全部楼层
嗯,我晚上仔细地研究了一下这个问题,并且查阅了一些以往osg-users上的资料,看来这并不是一个以往觉得简单的问题,事实上就连Paul Martz和J-S等浸淫多年的高手也为此犯憷,找不到一个足够合适的方案:

首先,ComputeBoundsVisitor得到的的确是父节点局部坐标系下的数据,但是我们并不能简单地用computeLocalToWorld或者getWorldMatrices将其转换到世界坐标系。因为当途径的某个父节点为MatrixTransform并且存在旋转时,这里的旋转矩阵会导致AABB包围盒被变换为OBB包围盒,因而计算结果也就是不准确的。为此,我们需要把包围体的8个顶点全都进行变换,例如:
  1. osg::ComputeBoundsVisitor boundvisitor;
  2. headTrans->accept(boundvisitor);
  3. osg::BoundingBox localBB = boundvisitor.getBoundingBox();
  4. osg::BoundingBox bb;
  5. osg::Matrix localToWorld =
  6.     osg::computeLocalToWorld( headTrans->getParent(0)->getParentalNodePaths()[0] );
  7. for (unsigned int i = 0; i < 8; i++)
  8. {
  9.     bb.expandBy(localBB.corner(i) * localToWorld);
  10. }
  11. boxBounding->setMatrix(osg::Matrix::scale(bb.xMax()-bb.xMin(),bb.yMax()-bb.yMin(),bb.zMax()-bb.zMin())
  12.     *osg::Matrix::translate(bb.center()));
复制代码
注意我们要把headTrans本身从ParentalNodePath中去掉,因为它已经被ComputeBoundsVisitor计算过了。因此这里用的是headTrans->getParent(0)。

上面的方法效率并不高,gamedev还介绍了另外一种算法,为了表示对楼主钻研精神的感谢,这里将其作了osg的转换和实现并列举如下:
  1. void transformBox( osg::Vec3& Min, osg::Vec3& Max, const osg::Matrix& Matrix )
  2. {
  3.     osg::Vec3 AMin, AMax;
  4.     float a, b;
  5.     int i, j;

  6.     // Copy box A into min and max array.
  7.     AMin = Min;
  8.     AMax = Max;

  9.     // Begin at T.
  10.     Min = Max = Matrix.getTrans();

  11.     // Find extreme points by considering product of
  12.     // min and max with each component of M.
  13.     for( j=0; j<3; j++ )
  14.     {
  15.         for( i=0; i<3; i++ )
  16.         {
  17.             a = Matrix(i,j) * AMin;
  18.             b = Matrix(i,j) * AMax;

  19.             if( a < b )
  20.             {
  21.                 Min[j] += a;
  22.                 Max[j] += b;
  23.             }
  24.             else
  25.             {
  26.                 Min[j] += b;
  27.                 Max[j] += a;
  28.             }
  29.         }
  30.     }
  31. }
复制代码
使用方法为:
  1. bb = boundvisitor.getBoundingBox();
  2. transformBox( bb._min, bb._max, headTrans->getParent(0)->getWorldMatrices()[0] );
  3. boxBounding->setMatrix(...);
复制代码

该用户从未签到

 楼主| 发表于 2010-12-23 09:31:29 | 显示全部楼层
3# array
谢谢您能够如此详尽地回复,我仔细学学并理会您的意思。如果有不明白的地方,再来请教您。

该用户从未签到

发表于 2010-12-23 10:14:27 | 显示全部楼层
学习了。呵呵~~~  的确讲的很明白呀!谢了!

该用户从未签到

发表于 2010-12-23 14:15:00 | 显示全部楼层
这个要顶一个!呵呵

该用户从未签到

 楼主| 发表于 2010-12-23 16:36:36 | 显示全部楼层
3# array
    您好,您的两种方案我都试验过了,结果如下:
    (1)第一种方案很好理解,代码也看的懂也可行,不过正如您所说效率不高,运行比较慢;其中有一个问题是
    osg::computeLocalToWorld( headTrans->getParent(0)->getParentalNodePaths()[0]
    前面的那个getParent(0)里面的参数0,我的理解是就只想得到它上一层的父节点,而不是它上两层、上三层的父节点,不知道对不对?另外,这句话中的最后为啥要取这个父节点队列中的第一个,也就是说为啥要有[0],不是应该取得它一直到根节点的路径上的所有节点么?因为如果场景树更深一些,很有可能它的上上层节点中也有MT节点啊。
    (2)您的第二个方案的代码,由于我基本功的原因,不是很理解,很汗颜。然后我运行了一下,报错,原因在于下面两句话
   a = Matrix(i,j) * AMin;

   b = Matrix(i,j) * AMax;
   我看解释说是这个*重载时没有重载这种类型的乘法。因为我不是很明白这段代码具体算法是什么意思,所以也不知道改怎么改写。
    如果您方便,还望您再给指点指点。

    万分感谢~~~~

该用户从未签到

发表于 2010-12-23 18:55:43 | 显示全部楼层
这个要学习了~~

该用户从未签到

发表于 2010-12-24 09:37:21 | 显示全部楼层
前面的那个getParent(0)里面的参数0,我的理解是就只想得到它上一层的父节点
OSG的场景图是DAG图的形式,而不是标准的树结构——因此一个节点可以有多个父节点(传统的树是单一父节点的),这样也很方便实现节点共享的功能。而getParent(0)表示取得第一个父节点,getNumParents()表示取得赴节点的数目。所有这些父节点都在同一个层次下。

如果您想取得更高层次的父节点,可以反复用getParent(0)->getParent(0)的形式,或者用getParentNodePaths()——即所有父节点路径——显而易见,路径也可能有多条(来自不同的父节点),而我们一般只需要考虑第一条就可以了(如果您知道自己的节点没有被多个父节点共享的话)

至于第二段程序,抱歉是我写漏了:
  1. a = Matrix(i,j) * AMin[i];
  2. b = Matrix(i,j) * AMax[i];
复制代码

该用户从未签到

 楼主| 发表于 2010-12-25 13:31:35 | 显示全部楼层
9# array 3QQ

该用户从未签到

发表于 2010-12-28 16:59:26 | 显示全部楼层
本帖最后由 wu_java 于 2010-12-28 17:04 编辑

3# array
按照您提供的方法我做了测试,左边的模型的父节点做了旋转动作,右边的没有做旋转动作
管子.JPG
怎样让左边的那个包围盒像右边的那样包围,提高精度?

该用户从未签到

 楼主| 发表于 2011-1-5 21:29:18 | 显示全部楼层
11# wu_java
    我也发现了这个问题,由于我对这方面要求不高,便没有再问。同盼解答。

该用户从未签到

发表于 2011-1-6 09:10:33 | 显示全部楼层
请给出基准状态,这张图里面的XYZ轴在什么方向?不然我看不懂您的图。此外哪个模型应用了我说的方法?那个方法的目的就是避免旋转对包围盒图形产生影响,如果您希望产生影响,那么直接将变换矩阵作用到min和max上即可

该用户从未签到

发表于 2011-3-8 09:22:20 | 显示全部楼层
好贴

该用户从未签到

发表于 2011-3-8 15:44:19 | 显示全部楼层
这个谁用了,整理个例子出来

该用户从未签到

 楼主| 发表于 2011-3-8 19:25:43 | 显示全部楼层
回复 15# tianxiao888


    您说的整理是指。。。

该用户从未签到

发表于 2011-3-8 20:50:29 | 显示全部楼层
就是整个画最小包围盒的例子,不随旋转改变

该用户从未签到

 楼主| 发表于 2011-3-8 22:55:54 | 显示全部楼层
回复 17# tianxiao888


    好的,我尽量找时间做个例子出来

该用户从未签到

 楼主| 发表于 2011-3-8 22:56:46 | 显示全部楼层
回复 18# lanxun1988


    斑竹,我还有个问题要请教您呢,在这个链接里面:http://bbs.osgchina.org/viewthread.php?tid=5075&extra=page%3D1,您有空给指点指点吧

该用户从未签到

发表于 2011-3-9 11:43:58 | 显示全部楼层
回复  tianxiao888


    好的,我尽量找时间做个例子出来
lanxun1988 发表于 2011-3-8 22:55



    感谢您的分享

该用户从未签到

发表于 2011-7-14 21:16:12 | 显示全部楼层
如果headTrans中还有个会变长的鼻子呢?鼻子节点长度变化肯定会导致包围盒改变,变长可以,缩回去就不行了

该用户从未签到

发表于 2011-7-26 10:48:49 | 显示全部楼层
回复 18# lanxun1988


    你的例子整理出来了吗,不随旋转改变的最小包围盒
您需要登录后才可以回帖 登录 | 注册

本版积分规则

OSG中国官方论坛-有您OSG在中国才更好

网站简介:osgChina是国内首个三维相关技术开源社区,旨在为国内更多的技术开发人员提供最前沿的技术资讯,为更多的三维从业者提供一个学习、交流的技术平台。

联系我们

  • 工作时间:09:00--18:00
  • 反馈邮箱:1315785073@qq.com
快速回复 返回顶部 返回列表