2013年8月10日 星期六

於Ogre中直接使用Bullet的好幫手~BtOgre~

OgreBullet已經摸了一週左右
雖然只寫不過三個範例:
其它外型的建立,旋轉力量的給予,與Constraint的建立。
但整體習性大概已知一二
大概剩下callback使用(碰撞處理與外力給予)、角色控制與車輛產生的部分吧
試到這裡就決定改用上一篇文章提過的BtOgre

BtOgre是個只有四個檔案構成的介面工具,
目前發掘它最大的用處有三點,就是處理:
1、entity轉shape
2、node轉btMotionState
3、debug用drawing在Ogre顯示
http://www.ogre3d.org/forums/viewtopic.php?f=5&t=46856
它支援的是原生的Bullet使用
換言之,要在Ogre上比較簡單的玩Soft Body應該只能靠它了
因為爬過OgreBullet的原始碼,它本身只有使用btDiscreteDynamicsWorld
而Sofy Body要用的是btSoftRigidDynamicsWorld
除非整個置換掉OgreBullet裡的,也就是大修原始碼,不然很難做到了。

BtOgre裡的demo比OgreBullet裡所寫的,簡單非常多
甚至也比bullet裡的還要明快直接
一看就可以知道要怎麼使用一步一步啟用bullet
很可惜是已經爬完bullet的demos後才看到的
省不了什麼學習的時間

BtOgre的使用範例如下
首先是物理世界的建立
/*
mBroadphase,mCollisionConfig,
mDispatcher,mSolver這些是創建world前必要的物件
通常是建在範例物件的被保護成員
至於用途的話,mBroadphase應該是物理世界的大小設定,
其它三者詳細就不清楚
phyWorld當然就是btDiscreteDynamicsWorld
*/
mBroadphase = new btAxisSweep3(
btVector3(-10000,-10000,-10000), 
btVector3(10000,10000,10000), 
1024);
mCollisionConfig = new btDefaultCollisionConfiguration();
mDispatcher = new btCollisionDispatcher(mCollisionConfig);
mSolver = new btSequentialImpulseConstraintSolver();

//上面設定好後,產生我們需要的動態世界
//使用上面建立的物件
phyWorld = new btDiscreteDynamicsWorld(mDispatcher, 
mBroadphase, 
mSolver, 
mCollisionConfig);
//設定整個世界的重力,通常是-9.81(m/s^2)
phyWorld->setGravity(btVector3(0,-9.81,0));

/*
dbgdraw是BtOgre的DebugDrawer,用來顯示shap外型線條的
其實整個BtOgre是從這邊才開始用,前面都是bullet的用法而已
基本上這個去掉也不影響動作
*/
dbgdraw = new BtOgre::DebugDrawer(mSceneMgr->getRootSceneNode(), phyWorld);
phyWorld->setDebugDrawer(dbgdraw);


建立一個可動的剛體的部分如下:
/*
既然使用Ogre,建立一個entity與node的物體是標準程序
*/
Ogre::Entity * ent;
Ogre::SceneNode* node;
ent = mSceneMgr->createEntity("FirstEnt","ogrehead.mesh");
ent->setCastShadows(true);
node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
node->attachObject(ent);
node->setPosition(0,10,0);

/*
再來使用物理引擎第一步,建立碰撞用外型(Shape)
這邊就需要BtOgre的幫忙把Entity的Mesh轉成Shape
這邊使用ConvexHull的 Shape
*/
BtOgre::StaticMeshToShapeConverter animeMesh(ent);
btCollisionShape* animeShape = animeMesh.createConvex();

/*
第二步給於物體質量與轉動慣量(inertia)
*/
btScalar mass = 10.0;
btVector3 inertia;
animeShape->calculateLocalInertia(mass, inertia);

/*
第三步建立RigidBodyState,我覺得可以視為node與bullet的連結
*/
BtOgre::RigidBodyState *rbState = new BtOgre::RigidBodyState(node);

//最後就是產生body
btRigidBody* defaultBody;
defaultBody = new btRigidBody(mass,rbState,animeShape,inertia);
//然後與世界做連結
phyWorld->addRigidBody(defaultBody);

//完成建立

之後就看你怎麼玩啦
不過不要忘了
要更新整個世界的運算,不然東西還是不會動的
要在FrameLisnter裡的frameStarted或frameEnded裡
加上這一行:phyWorld->stepSimulation(evt.timeSinceLastFrame);
這樣就能動了
不過如果有使用debugdraw則要加上另外兩項東西
以frameStarted的範例如下:
bool frameStarted(const FrameEvent &evt)
{
//更新物理狀態
phyWorld->stepSimulation(evt.timeSinceLastFrame);
//更新debugworld狀態
phyWorld->debugDrawWorld();

//原BtOgre有這一條,是按下F3時,才會顯示debugdraw
dbgdraw->setDebugMode(mKeyboard->isKeyDown(OIS::KC_F3));
//更新debugdraw的線條
dbgdraw->step();

return ExampleFrameListener::frameStarted(evt);
}


最後結束掉整個程式,或是重新建立物理世界時
記得照這順序進行刪除
1、先移除掉在world有用的所有的Constraint,再刪除
2、再移除掉所有的剛體,與btMotionState後,再刪掉
3、最後刪掉debugdrawer與wolrd,還有創建world的那堆物件
參考bullet與BtOgre的demo的綜合範例如下:
/*
這邊我用了bullet本身範例裡的處理法
先取得world本身的contraint數量
再一個一個從world移除後刪除
*/
for (int i=phyWorld->getNumConstraints()-1; i>=0 ;i--)
{
  btTypedConstraint* constraint = phyWorld->getConstraint(i);
  phyWorld->removeConstraint(constraint);
 delete constraint;
}
/*
這邊我用了OgreBullet本身範例裡的處理法
建立一組deque用來存建立過後RigidBody
最後用iterator的方法從world裡移除後刪除
*/
std::deque::iterator itBody = mBodies.begin();
while (mBodies.end() != itBody)
{
 phyWorld->removeRigidBody(*itBody);
 delete *itBody;
 ++itBody;
}

/*
Shape的部分也是用OgreBullet本身範例裡的處理法
建立一組deque用來存建立過後Shape
不同於RigidBody,它只要刪除就好了
*/
std::deque::iterator itShape = mShapes.begin();
while (mShapes.end() != itShape)
{
 delete *itShape;
 ++itShape;
}
//清除掉deque
mBodies.clear();
mShapes.clear();
//刪掉debugdraw與world
delete dbgdraw;
delete phyWorld;

//最後把建立world的物件全數刪除
delete mSolver;
delete mDispatcher;
delete mCollisionConfig;
delete mBroadphase;

接下來就是繼學習,然後好好寫個小遊戲之類的吧
其實還蠻想再修一下天車的說

沒有留言: