【3D技术宅公社】XR数字艺术论坛  XR技术讨论 XR互动电影 定格动画

 找回密码
 立即注册

QQ登录

只需一步,快速开始

调查问卷
论坛即将给大家带来全新的技术服务,面向三围图形学、游戏、动画的全新服务论坛升级为UTF8版本后,中文用户名和用户密码中有中文的都无法登陆,请发邮件到324007255(at)QQ.com联系手动修改密码

3D技术论坛将以计算机图形学为核心,面向教育 推出国内的三维教育引擎该项目在持续研发当中,感谢大家的关注。

查看: 2735|回复: 1

用OgreOde创建一个会走动的角色

[复制链接]
发表于 2008-12-2 17:05:30 | 显示全部楼层 |阅读模式

http://dev.gameres.com/Program/Visual/3D/OgreOdeCharacter.mht

 

第一版 by SuperMegaMau

这个教程包括的代码和算法是作者自己的经验,也许不正确或不怎么准确,如果发现问题请纠正。

 

内容

介绍

我相信我不是第一人自问如何用OgreOde创建一个运动角色。搜索论坛和wiki后,我意识到这是一个很有用的信息。这个教程解释了如何创建一个可以在地形上行走的运动角色(包括其它meshes,如树和房屋)。

创建物理模型

我按照在Monster的方法用下图代表一个角色:

下面,我假设你对Ogre的SceneNodes, meshes 和AlignedBoxes都有所了解,并且会用SceneManager创建地形。

创建角色

首先创建一个SceneNode来放角色的mesh,在这个例子中我用了Ogre例子中的忍者模型。创建两个SceneNode并把它们连在一起。后面我会解释为什么是两个Node。

Entity* ninja = mSceneMgr->createEntity("ninja","ninja.mesh");

SceneNode* ninjaNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("ninja");

SceneNode* modelNode = ninjaNode->createChildSceneNode("ninja_model");

modelNode->attachObject(ninja);

ninjaNode->setScale(0.05,0.05,0.05);

RootSceneNode

ninjaNode

modelNode

也许你已经注意到,ninjaNode被缩放得很小,这是因为如果mesh很大的话渲染的速度就变很慢(不知道为什么)。

获取 AABB(AxisAlignedBox, 轴对齐包围盒)

现在用AxisAlignedBox获取mesh的大小。

AxisAlignedBox aab = modelNode->getAttachedObject("ninja")->getBoundingBox();

Ogre::Vector3 min = aab.getMinimum()*ninjaNode->getScale();

Ogre::Vector3 max = aab.getMaximum()*ninjaNode->getScale();

Ogre::Vector3 center = aab.getCenter()*ninjaNode->getScale();

Ogre::Vector3 size(fabs(max.x-min.x),fabs(max.y-min.y),fabs(max.z-min.z));

float radius = (size.x>size.z)?size.z/2.0f:size.x/2.0f;

 

创建一个新空间

我们需要创建一个新空间把角色放在其中,并且取消内部碰撞检测。

OgreOde::SimpleSpace* dollSpace = new OgreOde::SimpleSpace(_world->getDefaultSpace());

dollSpace->setInternalCollisions(false);

创建球体(腿部)

现在有了两个SceneNodes, "ninjaNode" 和 "modelNode"。ninjaNode是代表你角色的节点,modelNode是你真正贴mesh的地方。这么做是因为mesh的中心总是在OgreOde::Body的中心,所以我们用ninjaNode来创建碰撞体的位置,然后根据ninjaNode和OgreOde::Body来获得mesh的正确位置。

左边图是用一个SceneNode所得到的效果,右边是用两个SceneNode。你可以注意到,左边的角色悬浮在半空中。下面代码创建了一个球体代表角色的脚部。我们需要一个SphereGeometry和一个TransformGeometry 将球体放到正确位置。

译注:注释为我的猜想,具体不知道步骤这么复杂,欢迎纠正。

(1)        创建一个碰撞体,命名为feet

(2)        设置碰撞体为球形物体,半径为AABB获得的半径

(3)        SphereGeometry,半径为AABB获得的半径

(4)        TransformGeometry,空间为刚才创建的空间, TransformGeometry似乎是为了包含某特定形状的几何体

(5)        改变modelNode相对于ninjiaNode的位置,以便让脚占到地上

(6)        让TransformGeometry包含一个OgreOde::Body和一个几何体

(7)        将Ogre::Body粘到ninjaNode上

 

 楼主| 发表于 2008-12-2 17:06:14 | 显示全部楼层
怀疑创建SphereGeometry是否只是让Ogre::Body具象化
 
OgreOde::Body* dollFeetBody = new OgreOde::Body("feet");  
dollFeetBody->setMass(OgreOde::SphereMass(70*2.5,radius));
OgreOde::SphereGeometry* feetGeom = new OgreOde::SphereGeometry(radius);
OgreOde::TransformGeometry* feetTrans = new OgreOde::TransformGeometry(dollSpace);
modelNode->translate(Vector3(0,-radius/ninjaNode->getScale().y,0));
feetTrans->setBody(dollFeetBody);
feetTrans->setEncapsulatedGeometry(feetGeom);
ninjaNode->attachObject(dollFeetBody);
创建椭球体
对于角色的上半身用一个椭球体来表示。
译注:和上面一样。
(1)        创建Ogre::Body
(2)        设置Ogre::Body形状,另外设置不被重力影响,
(3)        创建TransformGeometry,空间为刚才创建的空间
(4)        创建CapsuleGeometry,半径为AABB获得半径
(5)        设置CapsuleGeometry位置和方向和阻尼
(6)        让TransformGeometry包含Ogre::body
(7)        让TransformGeometry包含CapsuleGeometry
(8)        将Ogre::Body粘到ninjiaNode上
OgreOde::Body* dollTorsoBody = new OgreOde::Body("torso");
dollTorsoBody->setMass(OgreOde::CapsuleMass(70*2.5,radius,Vector3::UNIT_Y,radius));
dollTorsoBody->setAffectedByGravity(false);
dollTorsoBody->setDamping(0,50000);
OgreOde::TransformGeometry* torsoTrans = new OgreOde::TransformGeometry(dollSpace);
OgreOde::CapsuleGeometry* torsoGeom = new OgreOde::CapsuleGeometry(radius,size.y-4*radius,dollSpace);
torsoGeom->setPosition(Ogre::Vector3(0,size.y-((size.y-4*radius)/2+2*radius),0)); //can't find a good way to explain this
torsoGeom->setOrientation(Quaternion(Degree(90),Vector3::UNIT_X));
torsoTrans->setBody(dollTorsoBody);
torsoTrans->setEncapsulatedGeometry(torsoGeom);
ninjaNode->attachObject(dollTorsoBody);
这个几何体和脚的几何体在同一个空间,所以我们要取消内部碰撞检测。讲阻尼设置高些,并且取消重力,不然它会从那个球体上掉下来。
创建关节
剩下的事情就是将两个碰撞体连在一起了。一个绞连连接的代表是自行车前轮。
OgreOde::HingeJoint* joint = new OgreOde::HingeJoint();
joint->attach(dollTorsoBody,dollFeetBody);
joint->setAxis(Ogre::Vector3::UNIT_X);      //set the rotation axis
注意:不要忘记记录所有碰撞体和连接的位置以便你之后能得到它们。你可以用ogre堆栈或者创建你自己的。
移动角色
你可以通过不同方法移动或者旋转你的角色,我决定通过改变碰撞体方向而不是施加力或者力矩。
前后移动
下面代码可以放在按键响应里执行,象KC_UP。现在你需要获取碰撞体,从堆栈或者hashTable中获得,然后获取它的方向。我用:
译注:猜想堆栈就是你为屏幕上所有物体所创建的Ogre::Body的一个列表。
OgreOde::Body* torso = torsoBodies->getObject("ninja");
Quaternion q = torso->getOrientation();
然后赋予脚一个角速度。
OgreOde::Body* feet = feetBodies->getObject("ninja");
feet->wake();
feet->setAngularVelocity(q*Ogre::Vector3(10*cos(1),0,10*sin(1)));
10是我们用的角速度,必须乘以三角函数以便让角色向正确的方向前进。
左右转动
下面代码同样放在按键响应中执行,比如KC_RIGHT。
OgreOde::Body* torso = torsoBodies->getObject("ninja");
Quaternion q1 = torso->getOrientation();
Quaternion q2(Degree(-4),Ogre::Vector3::UNIT_Y);
torso->setOrientation(q1*q2);
用Degree(-4)让角色向右转动,用正数向左转动。也许你已经注意到,我总是从椭球体获取或者设置方向。我没有太多想,我想如果你从脚的球体来获取和设置也应该没有什么问题。
注意:如果你松开按键,你必须把速度设置为0来停止运动。
feetbody->setAngularVelocity(Vector3(0,0,0));
feetBody->setLinearVelocity(Vector3(0,feetBody->getLinearVelocity().y,0));
让角色爬起来
最后,我们要确定你的角色不摔倒,所以我们需要不时重新设定他的垂直方向。
OgreOde::Body* torso = torsoBodies->getObject("ninja");
Quaternion q = torso->getOrientation();                     
 
Vector3 x = q.xAxis();
Vector3 y = q.yAxis();
Vector3 z = q.zAxis();
 
torso->wake();
torso->setOrientation(Quaternion(x,Vector3::UNIT_Y,z));
问题
部分代码没有我想象的那么好。我重新设定垂直方向会让角色有奇怪的行为。虽然我设置了速度为0,但是角色在一些不规则的表面上仍然无法停下来。

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|3D数字艺术论坛 ( 沪ICP备14023054号 )

GMT+8, 2025-2-6 06:57

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表