查看: 3759|回复: 13

求助:键盘响应代码crash了

[复制链接]

该用户从未签到

发表于 2008-7-18 18:28:58 | 显示全部楼层 |阅读模式
我想实现像CS那样用键盘控制场景移动的,结果我一按键盘程序就崩溃了。关键代码如下:
  1. class PickHandler : public osgGA::GUIEventHandler
  2. {
  3. .................
  4.         bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
  5.         {
  6.                 osgViewer::Viewer* viewer = dynamic_cast<osgViewer::Viewer*>(&aa);
  7.                 if (!viewer) return false;
  8.                   ...................
  9.                         else if(ea.getKey()=='a')
  10.                         {
  11.                                 osg::ref_ptr<osg::Node> scene=viewer->getSceneData();
  12.                                
  13.                                 osg::ref_ptr<osg::MatrixTransform> tran=dynamic_cast<osg::MatrixTransform*> (viewer->getSceneData());
  14.                                 //tran->addChild(scene.get());
  15.                                 osg::Matrix m;
  16.                                 vx-=10;
  17.                                 m.makeTranslate(osg::Vec3(vx,0,0));
  18.                                 tran->setMatrix(m);
  19.                         }
  20.                    .................
  21.              }
  22. ...............
  23. }
复制代码
望各位帮忙解决一下啊~~

该用户从未签到

发表于 2008-7-18 20:37:47 | 显示全部楼层
回调……还是回调~~

千万注意OSG的三线程渲染方式以及由此而生的更新回调(UpdateCallback)和事件回调(EventCallback)机制。
事实上,一旦仿真主循环开始运行,也就是viewer.run()或者viewer.frame()开始执行之后,就尽量不要在回调以外的任何地方尝试修改场景图形的元素,纵然最简单的setMatrix也可能造成系统的崩溃。
原因很简单:用户线程(APP)和场景筛选线程(CULL),渲染线程(DRAW)都是周而复始地运行着的,上一次渲染线程结束之前,新的用户线程可能就已经开始了,线程内部可以保证此时数据共享的互斥性;但是在这三个线程之外更改数据的话,结果就是不可预知的~~
回调函数是在用户线程(APP)中被调用的,此时OSG系统已经为场景数据加锁,可以放心更改。

该用户从未签到

 楼主| 发表于 2008-7-21 09:45:35 | 显示全部楼层

加了回调函数了,还是有运行错误

已经加了回调函数了,还是会crash掉
  1. class PickHandler : public osgGA::GUIEventHandler
  2. {
  3. .................
  4.         bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
  5.         {
  6.                 osgViewer::Viewer* viewer = dynamic_cast<osgViewer::Viewer*>(&aa);
  7.                 if (!viewer) return false;
  8.                   ...................
  9.                         else if(ea.getKey()=='a')
  10.                         {
  11.                                 osg::ref_ptr<osg::Node> scene=viewer->getSceneData();
  12.                                 
  13.                                 osg::ref_ptr<osg::MatrixTransform> tran=dynamic_cast<osg::MatrixTransform*> (viewer->getSceneData());
  14.                                 //tran->addChild(scene.get());
  15.                                 osg::Matrix m;
  16.                                 vx-=10;
  17.                                 m.makeTranslate(osg::Vec3(vx,0,0));
  18.                                 //tran->setMatrix(m);以前是运行到这里就出错,现在注释掉了
  19.                                 tran->setUpdateCallback(new RotateCB);//运行到这里就出错了
  20.                         }
  21.                    .................
  22.              }
  23. ...............
  24. }
复制代码
回调类代码
  1. class RotateCB : public osg::NodeCallback
  2. {
  3. public:
  4.     RotateCB() : _angle( 0. ) {}

  5.     virtual void operator()( osg::Node* node,
  6.             osg::NodeVisitor* nv )
  7.     {
  8.         // Normally, check to make sure we have an update
  9.         //   visitor, not necessary in this simple example.
  10.         osg::MatrixTransform* mt = dynamic_cast<osg::MatrixTransform*>( node );
  11.         osg::Matrix m;
  12.         m.makeRotate( _angle, osg::Vec3( 0., 0., 1. ) );
  13.         mt->setMatrix( m );

  14.         // Increment the angle for the next from.
  15.         _angle += 0.01;

  16.         // Continue traversing so that OSG can process
  17.         //   any other nodes with callbacks.
  18.         traverse( node, nv );
  19.     }

  20. protected:
  21.     double _angle;
  22. };
复制代码

该用户从未签到

发表于 2008-7-21 20:11:19 | 显示全部楼层
检查一下
osg::ref_ptr<osg::MatrixTransform> tran=dynamic_cast<osg::MatrixTransform*> (viewer->getSceneData());
有没有得到正确的tran变量?

该用户从未签到

 楼主| 发表于 2008-7-22 09:44:00 | 显示全部楼层
的确是osg::ref_ptr<osg::MatrixTransform> tran=dynamic_cast<osg::MatrixTransform*> (viewer->getSceneData());没有正确匹配,问题出在哪里呢?
我试着重新写一个简单的例子,问题还是出在匹配上,帮看看问题在哪呀~~~
回调类,从《快速入门指导》里复制过来的:
  1. class RotateCB : public osg::NodeCallback
  2. {
  3. public:
  4.     RotateCB() : _angle( 0. ) {}

  5.     virtual void operator()( osg::Node* node,
  6.             osg::NodeVisitor* nv )
  7.     {
  8.         // Normally, check to make sure we have an update
  9.         //   visitor, not necessary in this simple example.
  10.         osg::MatrixTransform* mtLeft = dynamic_cast<osg::MatrixTransform*>( node );//应该没匹配到
  11.         osg::Matrix mR, mT;
  12.         mT.makeTranslate( -6., 0., 0. );
  13.         mR.makeRotate( _angle, osg::Vec3( 0., 0., 1. ) );
  14.         mtLeft->setMatrix( mR * mT );//debug的时候程序就卡在这里,mtLeft的指针为空,应该是上面没匹配到

  15.         // Increment the angle for the next from.
  16.         _angle += 0.01;

  17.         // Continue traversing so that OSG can process
  18.         //   any other nodes with callbacks.
  19.         traverse( node, nv );
  20.     }
  21. protected:
  22.     double _angle;
  23. };
复制代码
创建一个三角锥体:
  1. osg::ref_ptr<osg::Node> createSceneGraph(int a,int b,int h)
  2. {
  3.         // Create an object to store geometry in.
  4.         osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
  5.         // Create an array of four vertices.
  6.         osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array;
  7.         geom->setVertexArray( v.get() );
  8.         v->push_back( osg::Vec3( 0.f, -1.f, 0.f ) );
  9.         v->push_back( osg::Vec3( -1.f, 0.f, -1.f ) );
  10.         v->push_back( osg::Vec3( 1.f, 0.f, -1.f ) );
  11.         v->push_back( osg::Vec3( 1.f, 0.f, 1.f ) );
  12.         v->push_back( osg::Vec3( -1.f, 0.f, 1.f ) );
  13.         v->push_back( osg::Vec3( -1.f, 0.f, -1.f ) );
  14.         // Create an array of four colors.
  15.         osg::ref_ptr<osg::Vec4Array> c = new osg::Vec4Array;
  16.         geom->setColorArray( c.get() );
  17.         geom->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
  18.         c->push_back( osg::Vec4( 1.f, 0.f, 0.f, 1.f ) );
  19.         c->push_back( osg::Vec4( 0.f, 1.f, 0.f, 1.f ) );
  20.         c->push_back( osg::Vec4( 0.f, 0.f, 1.f, 1.f ) );
  21.         c->push_back( osg::Vec4( 1.f, 1.f, 0.f, 1.f ) );
  22.         c->push_back( osg::Vec4( 1.f, 0.f, 1.f, 1.f ) );
  23.         c->push_back( osg::Vec4( 0.f, 1.f, 0.f, 1.f ) );

  24.         // Draw a four-vertex quad from the stored data.
  25.         geom->addPrimitiveSet(new osg::DrawArrays( osg::PrimitiveSet::POLYGON, a,b ) );

  26.         osg::ref_ptr<osg::Geode> geode = new osg::Geode;
  27.         geode->addDrawable( geom.get() );
  28.         return geode.get();
  29. }
复制代码
主函数,创建场景:
  1. #include <osg/Geode>
  2. #include <osg/Geometry>

  3. #include <osgViewer/Viewer>
  4. #include <osgGA/TrackballManipulator>
  5. #include <osg/Camera>
  6. #include <osg/NodeCallback>
  7. #include <osg/Group>
  8. #include <osg/MatrixTransform>
  9. #include <osgDB/ReadFile>
  10. #include <osg/Notify>

  11. int main( int, char ** )
  12. {

  13.         osg::ref_ptr<osg::Group> root =new osg::Group;
  14.         root->addChild(createSceneGraph(0,6,-1).get());//画三角锥体的上面部分
  15.         root->addChild(createSceneGraph(1,4,1).get());//画三角锥体的下面部分(就是那个正方型的盖子)
  16.         osg::StateSet* state = root->getOrCreateStateSet();
  17.         state->setMode( GL_LIGHTING,osg::StateAttribute::OFF |osg::StateAttribute::PROTECTED );
  18.         root->setDataVariance( osg::Object::DYNAMIC );//将节点设为DYNAMIC告诉OSG我要在运行的时候更改它
  19.         root->setUpdateCallback(new RotateCB);//设置回调函数,不用的时候可以正常运行

  20.     // Create the viewer and set its scene data to our scene
  21.     //   graph created above.
  22.     osgViewer::Viewer viewer;
  23.         viewer.setSceneData(root.get());
  24.     if (!viewer.getSceneData())
  25.         return 1;

  26.     // Loop and render. OSG calls RotateCB::operator()
  27.     //   during the update traversal.
  28.         viewer.getCamera()->setProjectionMatrixAsPerspective(80., 1., 1., 100. );
  29.         // Create a matrix to specify a distance from the viewpoint.
  30.         osg::Matrix trans;
  31.         trans.makeTranslate( 0., 0., -12. );
  32.         // Rotation angle (in radians)
  33.         double angle=0.0;
  34.         while (!viewer.done())
  35.         {
  36.                 // Create the rotation matrix.
  37.                 osg::Matrix rot;
  38.                 rot.makeRotate( angle, osg::Vec3( 1., 0., 0. ) );
  39.                 angle += 0.01;
  40.                 // Set the view matrix (the concatenation of the rotation and
  41.                 // translation matrices).
  42.                 viewer.getCamera()->setViewMatrix( rot * trans );
  43.                 // Draw the next frame.
  44.                 viewer.frame();
  45.         }
  46. }
复制代码
以上代码在VC2005SP1+OSG2.4+Platform SDK 2003下编译通过。

[ 本帖最后由 Sailent 于 2008-7-22 09:53 编辑 ]

该用户从未签到

发表于 2008-7-22 13:21:40 | 显示全部楼层
好像您的root变量不是MatrixTransform指针类型,而是Group类型的节点吧?这样使用dynamic_cast<>得到的必然是无法匹配的结果。
不妨在root下添加一个矩阵变换节点,并设置这个节点的更新回调为RotateCB

该用户从未签到

 楼主| 发表于 2008-7-22 17:42:21 | 显示全部楼层
你的意思是把main里面的
  1. root->addChild(createSceneGraph(0,6,-1).get());
  2. root->addChild(createSceneGraph(1,4,1).get());
  3. ..............
  4. root->setDataVariance( osg::Object::DYNAMIC );
  5. root->setUpdateCallback(new RotateCB);
复制代码
改为:
  1. osg::ref_ptr<osg::MatrixTransform> MatrixTrans=new osg::MatrixTransform;
  2. MatrixTrans->addChild(createSceneGraph(0,6,-1).get());
  3. MatrixTrans->addChild(createSceneGraph(1,4,1).get());
  4. root->addChild(MatrixTrans.get());
  5. ..............
  6. MatrixTrans->setDataVariance( osg::Object::DYNAMIC );
  7. MatrixTrans->setUpdateCallback(new RotateCB);
复制代码
对吗?
我试验了一下,这样的确没问题了,但是有2个问题:
1.如果root是从文件里读取的,怎么在root下添加MatrixTransform子节点?
2.如果root的节点类型是node,如何添加子节点?比如以下代码:
  1. else if(ea.getKey()=='a')
  2. {
  3.      osg::ref_ptr<osg::MatrixTransform> tran=new osg ::MatrixTransform;
  4.      osg::ref_ptr<osg::Node> root=viewer->getSceneData();
  5. //     root->addChild(tran);不能这样做,因为node类没有addChild这个方法。
  6. //     root->setUpdateCallback(new RotateCB);显然我们也不能这样做,否则在回调函数里面会出错
  7. }
复制代码

该用户从未签到

发表于 2008-7-22 19:27:30 | 显示全部楼层
第一个问题:
没有区别,使用addChild是可以的
第二个问题:
如果您已经知道root的节点类型的话(比如Group),可以直接用dynamic_cast<osg::Group>( viewer->getSceneData() )
如果不知道的话,不妨先用className()函数获取root对象的类型名,Group节点的返回值是字符串"Group",而MatrixTransform返回的则是"MatrixTransform"

[ 本帖最后由 array 于 2008-7-22 19:28 编辑 ]

该用户从未签到

 楼主| 发表于 2008-7-25 15:10:52 | 显示全部楼层
感谢array的帮助,现在程序已经可以正常运行了。不过程序还是有一些问题,它并不能做到像CS游戏那样的自由移动。例如:1.A,D是左右移动,我按完A再立刻按D,场景会停个一会再往右移动;2.一直按着A可以向左移动,但按一下D再放开(此时仍然按着A),场景就不动了。程序已通过附件形式上传,用VC2005编译后可直接运行,有时间的话帮忙看看呀~

test17.rar

11.02 KB, 下载次数: 117, 下载积分: 威望 1

场景漫游,编译后可直接运行

该用户从未签到

发表于 2008-7-25 17:36:25 | 显示全部楼层
应该是键盘响应程序的问题,我用的是海军教程的键盘响应,感觉还顺畅。

该用户从未签到

 楼主| 发表于 2008-7-28 09:14:36 | 显示全部楼层
你说的海军教程的键盘响应是那个卡车的例子吗?我就是根据那个例子改的。那个例子并没有根据键盘控制场景移动的操作,而是通过鼠标控制的,所以你才会感觉顺畅。如果是键盘控制的,情况可能就不一样了。

该用户从未签到

发表于 2008-7-28 13:01:15 | 显示全部楼层
不是卡车的例子,是坦克的例子,海军教程里好像没有卡车吧,只有一个坦克,里面的键盘响应做成了一个类。

该用户从未签到

 楼主| 发表于 2008-8-5 12:43:41 | 显示全部楼层
原帖由 moumoumou 于 2008-7-28 13:01 发表
不是卡车的例子,是坦克的例子,海军教程里好像没有卡车吧,只有一个坦克,里面的键盘响应做成了一个类。

今天专门试验了一下你说的那个例子(叫tutor9对吧),情况是一样的,你一直按着J,坦克的头会一直向左转动,但如果你按一下K(仍然按着J不放),坦克的头就不动了,尽管你仍然按着J

该用户从未签到

发表于 2008-8-5 14:26:25 | 显示全部楼层
原帖由 Sailent 于 2008-8-5 12:43 发表

今天专门试验了一下你说的那个例子(叫tutor9对吧),情况是一样的,你一直按着J,坦克的头会一直向左转动,但如果你按一下K(仍然按着J不放),坦克的头就不动了,尽管你仍然按着J


这个跟OSG就没有直接关系了,通常的键盘处理方式是处理不了这种问题的。比如在Windows的记事本中做同样的事情,您就会发现这个现象一样存在。
建议自己编写专门的键盘缓冲区程序,来处理多个按键同时按下以及放开的情况
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

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

联系我们

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