OgreODE入门

既然用了Ogre,那么一定是要有一个物理引擎的。最后选择了ODE,因为ODE在持续更新,文档稍微要多一些。

但是中文文档基本没有,为了以后能快速阅读,只好自己先翻一下了。

这只是一个介绍性的文档,其中的代码并不能形成一个完整的可执行程序。如果结合着OgreODE的Demo的代码看这篇文章,效果会很好的。

原文:http://www.ogre3d.org/wiki/index.php/First_steps_with_OgreODE

这是一个为OgreODE初学者所写的一个入门教程。因为这里(OGRE wiki)没有OgreODE的详尽的文档,所以写了这个教程,希望能对初学者有所指引和帮助。SimpleScenes是另一个学习的好地方,那里有7个带有实际代码的教程。本教程的所有代码都是从那7个教程里节选的。

一、初始化OgreODE,创建一个最简单的物理世界

在我们开始学习之前,你需要准备好:

1.带有一个sceneManager、一个camera、一些basic light的可工作的Ogre程序

2.OgreODE和ODE头文件

3.一个带有material and texture 的 Ogre mesh,比如一个平面或者一条街道之类的(基础世界)

4.一个盒装的带有material and texture 的 Ogre mesh(世界中的物体)

如果你学习完了Ogre Tutorials,那么你能很轻松地做到这些。

在一个最好是你申明了Ogre::Root的头文件里,包含上OgreODE的头文件,并申明以下变量:

#include "OgreOde/OgreOde_Core.h"
OgreOde::World                       *mWorld;
OgreOde::Space                       *mSpace;
OgreOde::StepHandler                 *mStepper;

OgreOde::World类似Ogre::Root,是OgreODE最顶层的对象。你所添加的每一个类,都会以某种方式添加到这个world中,因此这个世界中的物理定律就可以应用到添加的这个类上。但是要小心了,现在版本的OgreODE允许在一个程序中存在多个world。不过这种情况,只有在你确实需要复杂的"Prey-style"的重力效果时或做一些进阶性的优化时,才可能出现。

OgreOde::Space(碰撞空间)类似于一个所有对象(集合体更加特殊)的集合。这些对象可以与自己所在的space以及其它的space里的对象相碰撞,也可以不和任何对象发生碰撞。在大多数情况下,你将会创建一个有等级的碰撞空间——最重要节点的在顶部,很多次要的子节点附属在其下面。

在ODE里面有三种类型的碰撞空间——碰撞空间(Collision Space)、简单空间(Simple Space)和象限四分树空间(Quadtree Space)。碰撞空间和简单空间的差别很小(举个例子,设想碰撞空间是“根”空间,而简单空间是类似人物的手臂一样的“子”空间),而象限四分树空间是特别为大型项目的优化而设计的。所以如果你的场景有大于1000平方米的时候,象限四分树能提高10-20%的性能。(问题是,因为一些bug,象限四分树在OgreOde中还没有得到完好地实现,但这些bug是能被胸怀大志的开发者所修正的。)

OgreOde::StepHandler用于处理world中的时间,你将会使用它来更新事件(也就是说,stephandler时刻(比如每一帧)都在运行着,并且决定着哪一个物体运动到哪里以及与什么发生碰撞)。

二、改写你的.cpp文件

现在找到头文件所对应的cpp文件,让我们来创建一个华丽的新世界。

mWorld = new OgreOde::World(mSceneMgr);
mWorld->setGravity(Ogre::Vector3(0,-9.80665,0));
mWorld->setCFM(10e-5);
mWorld->setERP(0.8);
mWorld->setAutoSleep(true);
mWorld->setAutoSleepAverageSamplesCount(10);
mWorld->setContactCorrectionVelocity(1.0);
mSpace = mWorld->getDefaultSpace();

正如你所看到的,world注册到了场景管理器(scene manager),同时设定了重力加速度为9.81。这里请注意两个事情:重力加速度设为了“负”,也就是说对象是“下落”而不是“上浮”;关于世界单位——如果你使用的是“1 ogre 单位 = 1 米”那么重力加速度应设为-9.80665,如果你使用的是“1 ogre 单位 = 1 厘米”(也就是说缩小了100倍)那么重力加速度就应该设为-980.665(相应扩大了100倍)。

后面的两行(setCFM 和 setERP)的设定,影响了模拟现实中的的关节、弹力和摩擦等方面。这里不详细讨论这两个函数,就像代码一样写就可以了。

setAutoSleep设定了world启用它的自动优化(即如果一个对象不再移动,它就会停止重复移动)。

setAutoSleepAverageSamplesCount,用于修正AutoSleep。最好就是设其参数为10,我们将在后面来讨论这个。

setContactCorrectionVelocity,一个很重要的设定。如果不设置它的话,对象间可能会相互穿透。

下一步,添加如下代码:

const Ogre::Real _time_step = 0.5;
   const Ogre::Real time_scale = Ogre::Real(1.7);
   const Ogre::Real max_frame_time = Ogre::Real(1.0 / 4);
   mStepper = new OgreOde::StepHandler(mWorld, 
                                  OgreOde::StepHandler::QuickStep,
                                  _time_step, 
                                  max_frame_time, 
                                  time_scale);

最后一步是设定Stepper。你可以在StepHandler::QuickStep, StepHandler::BasicStep 和 StepHandler::FastStep中选择一种,但这里不做详细说明。你也可以在其它的不同类型的StepHandler中选择,但是这也是会在后面提到(尽管这个很重要)。

现在,我们有了一个带有重力的世界,但在这个世界里面什么东西都没有。让我们来改写它:

三、用一个盒子和一个平面来测试我们的重力系统

三、用一个箱子和一个平面来测试我们的重力系统

让我们在世界中添加一个平面和一个箱子(Crate)的实体/节点/主体,我们可以让这个箱子在我们设定的重力系统中进行自由落体运动。

在头文件中加入以下变量:

OgreOde::InfinitePlaneGeometry *mGround;    
   OgreOde::Body                  *mBody;
   OgreOde::Geometry              *mGeom;
   OgreOde::BoxMass               mMass;
   Ogre::SceneNode                *mNode;
   Ogre::Entity                   *mEntity;

你应该已经知道最后两行的意思了。这些变量的第一个将会是ground上的平面,后面的三个变量是关于箱子的。

回到cpp文件,为创建平面添加如下代码:

mGround = new OgreOde::InfinitePlaneGeometry(
                               Plane(Ogre::Vector3(0,1,0),0), 
                               mWorld, mWorld->getDefaultSpace());
   // Use a load of meshes to represent the floor
   int i = 0;
   StaticGeometry* s;
   s = mSceneMgr->createStaticGeometry("StaticFloor");
   s->setRegionDimensions(Ogre::Vector3(160.0, 100.0, 160.0));
   // Set the region origin so the center is at 0 world
   s->setOrigin(Ogre::Vector3::ZERO);
   for (Real z = -80.0;z <= 80.0;z += 20.0)
   {
   for (Real x = -80.0;x <= 80.0;x += 20.0)
   {
      String name = String("Ground") + StringConverter::toString(i++);
      Entity* entity = mSceneMgr->createEntity(name, "plane.mesh");
      entity->setQueryFlags (1<<4);
      entity->setUserObject(mGround);
      entity->setCastShadows(false);
      s->addEntity(entity, Ogre::Vector3(x,0,z));
   }
   }
   s->build();

这里创建的一个位于x-z面的平面。现在来创建箱子,首先我们需要常规的Ogre/Entity/Node:

mEntity = mSceneMgr->createEntity("crate","crateCube.mesh");
 mEntity->setQueryFlags (1<<2);
 mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("crate");
 mNode->attachObject(mEntity);
 mEntity->setNormaliseNormals(true);
 mEntity->setCastShadows(true);

然后创建箱子的body并将其“注册”到world和场景节点(scene node):

mBody = new OgreOde::Body(mWorld);
 mNode->attachObject(mBody);

然后我们设定箱子的体积和质量。因为这是一个盒装的对象,所以我们设定OgreOde::BoxGeometry。其它的比如Sphere或者TriangleMesh在OgreODE也同样有效。

Vector3 size(10.0,10.0,10.0);
OgreOde::BoxMass mMass(0.5,size);
mMass.setDensity(5.0,size);
mGeom = (OgreOde::Geometry*)new OgreOde::BoxGeometry(
                                            size, mWorld, mSpace);
mNode->setScale(size.x * 0.1,size.y * 0.1,size.z * 0.1);
mBody->setMass(mMass);
mGeom->setBody(mBody);
mEntity->setUserObject(mGeom);

最后我们设定箱子的方向和位置。把它放置到摄像机(camera)前,方便看到它自由落体。

mBody->setOrientation(Quaternion(Radian(5.0),Ogre::Vector3(0,0,0)));
mBody->setPosition(Vector3(0,120,-20));

现在就有了一个悬在空中的箱子了。我们需要更新world的time steps。添加如下代码到主要的循环或gamestate::update 或你更新当前帧的地方:

Ogre::Real time = 0.1;
    if (mStepper->step(time))
    {
        mWorld->synchronise();
    }

如果你的代码里有一个类似"TimeSinceLastFrame"的值,设定"time"为这个值。现在就可以编译了,尝试去实现它!

如果你只看到一毫秒的急速运动的棕色的点,尝试将重力加速度除以100或1000,使用0.00981代替9.81。这个箱子应该会在这一点穿过平面,因为我们根本没有处理碰撞。关于碰撞的处理你可以在这里找到OgreOde Collision Handling

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值