bullet物理引擎btBvhTriangleMeshShape,btHeightfieldTerrainShape的使用

Bullet提供了幾個類btBvhTriangleMeshShapebtHeightfieldTerrainShape去創建一些網格圖形,首先了解btHeightfieldTerrainShape,通過高度圖數據創建一個3D地形。

A static mesh that is optimised for and described by the surface of a height map.

官網解釋:http://bulletphysics.com/Bullet/BulletFull/classbtHeightfieldTerrainShape.html#a90d823ba5f44871a0bcfce0174177223

建議先閱讀官網介紹

首先可以下幾個效果圖


根據高度圖數據.raw生成的高度地形圖

參數設置HeightfieldInfo info(128, 128, _heightMapData.getBytes(), PHY_UCHAR, 1.6f / uData, -1.f, 1.f, btVector3(25.f / uData, 1.f, 25.f / uData));

(uData_heightMapData的最大值)


自定義數據生成高度地形圖(PHY_FLOAT)

參數設置HeightfieldInfo info(128, 128, mapData, PHY_FLOAT, 1.f, -1.f, 1.f, btVector3(1.f, 1.f, 1.f));

mapData自定義數據,隨機0~1的數據



自定義數據生成高度地形圖(PHY_FLOAT)

參數設置HeightfieldInfo info(128, 128, mapData, PHY_SHORT, 1.f, -1.f, 1.f, btVector3(1.f, 1.f, 1.f));

mapData自定義數據0,1的數據


Bullet 自帶的Demo中的例子


btHeightfieldTerrainShape 有兩個構造函數,這裏分析較複雜的一個

 

btHeightfieldTerrainShape(

int heightStickWidth,   x軸總寬度

int heightStickLength,  z軸總長度

比如 width = 128, length = 64 則x軸方向爲128,z軸方向爲64

const void* heightfieldData,  高度數據

btScalar heightScale, 每個字節*heightScale = 實際高度

btScalar minHeight,  最小高度

btScalar maxHeight, 最大高度 

地形原點 = (minHeight + maxHeight) * 0.5

int upAxis,  方向軸 取值 0=x, 1=y, 2=z,決定地形的朝向,類似法向量

PHY_ScalarType heightDataType, 數據格式 3種, PHY_UCHAR, PHY_SHORT, PHY_FLOAT

bool flipQuadEdges 方形裁剪

);


舉個例子

50*50 數據 

for (int i=0; i<50; ++i)

{

for (int j=0; j<50; ++j)

{

heightMap[i*50+j] = j % 2;

}

}

對於heightMap[i*50+j] 

1.如果爲0, minHeight = 0.f, maxHeight = 6.f;

  最低點正好爲-3.f

2.如果爲0, minHeight = 0.f, maxHeight = 12.f;

  最低點正好爲-6.f

3.如果爲0, minHeight = 0.f, maxHeight = 3.f;

  最低點正好爲-1.5f

 

1.如果爲2, minHeight = 0.f, maxHeight = 6.f;

  最低點正好爲-4.f

2.如果爲2, minHeight = 0.f, maxHeight = 12.f;

  最低點正好爲-7.f

3.如果爲2, minHeight = 0.f, maxHeight = 3.f;

  最低點正好爲-2.5f

 

地形偏移offsetY =  -(minHeight + maxHeight);

不推薦minHeight + maxHeight < 0, 不穩定

heightScale * value(heightfieldData[i])爲實際高度

 

高度計算:

對於PHY_UCHAR

最低點y = offsetY + min(heightfieldData); minY = 0

最高點y = offsetY + max(heightfieldData) * heightScale;

 

對於PHY_SHORT, PHY_FLOAT

最高點y = offsetY + max(heightfieldData) * heightScale;

最低點y = offsetY + min(heightfieldData) * heightScale;

 

注意:

網格間隔不要過大,過大會出現物體穿過。

 

自定義數據類型簡化參數傳遞

 
  1. struct HeightfieldInfo  
  2. {  
  3.     int heightStickWidth;  
  4.     int heightStickLength;  
  5.     void* heightfieldData;  
  6.     PHY_ScalarType hdt;  
  7.     btScalar heightScale;  
  8.     btScalar minHeight;  
  9.     btScalar maxHeight;  
  10.     int upAxis;  
  11.   
  12.     bool useFloatData;  
  13.     bool flipQuadEdges;  
  14.     btVector3 localScaling;  
  15.   
  16.     HeightfieldInfo(int width, int length, void* data, PHY_ScalarType type = PHY_SHORT,   
  17.                     btScalar heiScale = 1.f, btScalar minHei = 0.f, btScalar maxHei = 1.f,   
  18.                     const btVector3& scale = btVector3(1, 1, 1), int up = 1,   
  19.                     bool useFloat = falsebool filpQuad = false) :  
  20.         heightStickWidth(width), heightStickLength(length), heightfieldData(data),   
  21.             heightScale(heiScale), minHeight(minHei), maxHeight(maxHei),   
  22.             localScaling(scale), upAxis(up),   
  23.             hdt(type), useFloatData(useFloat), flipQuadEdges(filpQuad)  
  24.     {}  
  25. };  

PhysicsWorld3D 創建高度地形圖

 

  1. btRigidBody* PhysicsWorld3D::addHeightfieldTerrain(const HeightfieldInfo& fieldInfo, const btVector3& position, const PhysicsMaterial3D& material)  
  2. {  
  3.     CCAssert(material.mass == 0.f, "height field's mass must be 0.");  
  4.   
  5.     btHeightfieldTerrainShape* heightfieldShape = new btHeightfieldTerrainShape(  
  6.         fieldInfo.heightStickWidth, fieldInfo.heightStickLength, fieldInfo.heightfieldData, fieldInfo.heightScale,  
  7.         fieldInfo.minHeight, fieldInfo.maxHeight, fieldInfo.upAxis, fieldInfo.hdt, fieldInfo.flipQuadEdges);  
  8.   
  9.     heightfieldShape->setUseDiamondSubdivision(true);    // 鑽石細分矩形方格會出現對角線  
  10.     heightfieldShape->setLocalScaling(fieldInfo.localScaling);  
  11.   
  12.     auto body = getBody(heightfieldShape, position, material);  
  13.   
  14.     _world->addRigidBody(body);  
  15.     return body;  
  16. }  

 

下面來介紹btBvhTriangleMeshShape,通過載入三角網格,實現網格形狀的物理模擬

http://bulletphysics.com/Bullet/BulletFull/classbtBvhTriangleMeshShape.html

 

看看效果


地形能夠與模型完美的融合在一起,而且即使半徑爲0.1的球體也不會穿過地形

使用的shape就是btBvhTriangleMeshShape, 構造方法有兩個

btBvhTriangleMeshShape(

btStridingMeshInterface* meshInterface,  // 網格接口,存放網格數據

bool useQuantizedAabbCompression,// 壓縮?只有buildBvh爲true纔有效

const btVector3& bvhAabbMin,

const btVector3& bvhAabbMax, // mesh不可超過bvhaabb包圍盒,只有buildBvh爲true纔有效

bool buildBvh = true);// 優化BVH


btBvhTriangleMeshShape(

btStridingMeshInterface* meshInterface, 

bool useQuantizedAabbCompression, 

bool buildBvh = true);

 

通過導入一個模型的原始三角形數據,就可以建立上圖的地形

如何載入模型數據,官網類關係圖


提供btTriangleIndexVertexArray,載入網格數據

btTriangleIndexVertexArray(

int numTriangles, // 三角個數

int* triangleIndexBase, // 三角形索引數組首地址

int triangleIndexStride, // 每個三角形索引大小 = 索引類型大小 * 3

int numVertices, // 頂點個數

btScalar* vertexBase, // 頂點數組首地址

int vertexStride); // 每個頂點字節 = 頂點元素 * 3

 

既然索引類型爲int,就用int

 

關於原始三角形數據如何得到,

1.可以利用cocos2dx的載入模型函數獲取(有待實驗)

2.利用Blender或者可以導出模型原始三角數據的軟件,直接導出數據

關於Blender一款開源的3D建模軟件,官網:http://www.blender.org/ , 自帶遊戲引擎,物理引擎就是Bullet

 

導出三角形數據,Blender有個插件專門導出三角形數據 文件後綴名爲raw,它是文本格式的,

導出時首先要讓模型旋轉一定角度,座標系不是opengl的座標系,cocos2dx採用的就是opengl的座標系

raw文件格式非常簡單:n,每行9個浮點數據(描述一個三角形),每三個浮點爲一個頂點

 

3.自定義格式

。。。。

 

來實現數據的載入吧

首先讀取raw文件,實現一個簡單的PhysicsHelper3D

 
  1. #ifndef __PHYSICS_HELPER_3D_H__  
  2. #define __PHYSICS_HELPER_3D_H__  
  3. #include <cocos2d.h>  
  4. USING_NS_CC;  
  5.   
  6. class PhysicsHelper3D  
  7. {  
  8. public:  
  9.     static std::vector<float> loadRaw(const char* fileName);  
  10.     static bool loadRaw(const char* fileName, std::vector<float>& verts);  
  11. };  
  12.   
  13. #endif // !__PHYSICS_HELPER_3D_H__  
  14.   
  15.   
  16. #include "PhysicsHelper3D.h"  
  17.   
  18. std::vector<float> PhysicsHelper3D::loadRaw(const char* fileName)  
  19. {  
  20.     std::vector<float> data;  
  21.     if (loadRaw(fileName, data))  
  22.     {  
  23.         return data;  
  24.     }  
  25.   
  26.     return std::vector<float>(0);  
  27. }  
  28.   
  29. bool PhysicsHelper3D::loadRaw(const char* fileName, std::vector<float>& verts)  
  30. {  
  31.     char line[1024];  
  32.       
  33.     float oneData;  
  34.     auto rawData = FileUtils::getInstance()->getStringFromFile(fileName);    // 利用cocos2dx載入文件  
  35.     std::stringstream ss, ssLine;  
  36.     ss << rawData;              
  37.     while (ss.getline(line, 1024))  // 讀取一行  
  38.     {  
  39.         ssLine << line;  
  40.         for (int i = 0; i < 9; i++)  // 獲取9個浮點數  
  41.         {  
  42.             ssLine >> oneData;  
  43.             verts.push_back(oneData);  
  44.         }  
  45.     }  
  46.     return true;  
  47. }  

並不是很難吧,載入文件辦法不好,不過先將就着用吧

 
  1. _indexVertexArrays = new btTriangleIndexVertexArray(_verts.size() / 9, &_verIndices[0], 3 * sizeof(int),  
  2.                     _verts.size() / 3, (btScalar*)&_verts[0], 3 * sizeof(float));  
  3. _meshShape = new btBvhTriangleMeshShape(_indexVertexArrays, true);  
  4.   
  5. _verts是vector<float> 三角形的個數 =_verts.size() / 9  
  6.   
  7. 爲了構建方便實現PhysicsMesh3D  
  8.   
  9.   
  10. #ifndef __PHYSICS_MESH_3D_H__  
  11. #define __PHYSICS_MESH_3D_H__  
  12. #include "Bullet/btBulletDynamicsCommon.h"  
  13. #include "cocos2d.h"  
  14. USING_NS_CC;  
  15.   
  16. class PhysicsMesh3D  
  17. {  
  18. public:  
  19.     static PhysicsMesh3D* constuct(const char* fileName);  
  20.       
  21.     void destroy();  
  22.     bool initWithFile(const char* fileName);  
  23.   
  24. private:  
  25.     std::vector<float> _verts;        // 存放頂點  
  26.     std::vector<int> _verIndices; // 頂點索引  
  27.     btTriangleIndexVertexArray* _indexVertexArrays; // 三角形數據  
  28.     CC_SYNTHESIZE_READONLY(btBvhTriangleMeshShape*, _meshShape, MeshShape); // shape  
  29. };  
  30.   
  31. #endif  
  32.   
  33. CC_SYNTHESIZE_READONLY 爲cocos2dx提供的宏  
  34.   
  35. bool PhysicsMesh3D::initWithFile(const char* fileName)  
  36. {  
  37.     _indexVertexArrays = nullptr;  
  38.     _verts.clear();  
  39.     _verIndices.clear();  
  40.     if (PhysicsHelper3D::loadRaw(fileName, _verts))     // 載入數據  
  41.     {  
  42.         _verIndices.resize(_verts.size());          // 頂點的位置就是索引  
  43.         for (int i=0; i < _verts.size(); ++i)  
  44.         {  
  45.             _verIndices[i] = i;  
  46.         }  
  47.         _indexVertexArrays = new btTriangleIndexVertexArray(  
  48. _verts.size() / 9,      // 三角形個數  
  49. &_verIndices[0],        // 三角數據數組首地址  
  50. 3 * sizeof(int),        // 一個三角索引大小  
  51.                     _verts.size() / 3,      // 頂點個數  
  52. (btScalar*)&_verts[0],  // 頂點數組首地址  
  53. 3 * sizeof(float));     // 一個頂點大小  
  54. // 獲取shape  
  55.         _meshShape = new btBvhTriangleMeshShape(_indexVertexArrays, true);  
  56.   
  57.         return true;  
  58.     }  
  59.   
  60.     return false;  
  61. }  

釋放申請的內存

 
  1. void PhysicsMesh3D::destroy()  
  2. {  
  3.     _verts.clear();  
  4.     _verIndices.clear();  
  5.     delete _indexVertexArrays;  
  6.     delete this;  
  7. }  

在PhysicsWorld3D建立一個添加Mesh的方法

 
  1. btRigidBody* addTriangleMesh(PhysicsMesh3D* mesh3D, const btVector3& position,   
  2. const PhysicsMaterial3D& material = PHYSICS_MATERIAL3D_PLANE);  
  3.   
  4. btRigidBody* PhysicsWorld3D::addTriangleMeshShape(PhysicsMesh3D* mesh3D, const btVector3& position, const PhysicsMaterial3D& material)  
  5. {  
  6.     CCAssert(material.mass == 0.f, "body's mass must be 0.");  
  7.   
  8.     auto body = getBody(mesh3D->getMeshShape(), position, material);  
  9.     _world->addRigidBody(body);  
  10.   
  11.     return body;  
  12. }  

測試

HelloWorld 添加變量

 
  1. PhysicsMesh3D* _phyMesh3D;  // mesh shape  
添加網格
 
  1. _phyMesh3D = PhysicsMesh3D::constuct("heightmap.raw");  
  2. _world->addTriangleMesh(_phyMesh3D, btVector3(0, 0, 0));  
  3.   
  4. // 載入plane模型  
  5. auto spPlane = Sprite3D::create("model/heightmap.c3b");   
  6. this->addChild(spPlane);  
  7. spPlane->setPosition3D(Vec3(0, 0, 0));  
  8. spPlane->setRotation3D(Vec3(0, 180, 0));  

onExit()不要忘了

 
  1. _phyMesh3D->destroy();  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章