Bullet(Cocos2dx)之创建地形
Bullet提供了几个类btBvhTriangleMeshShape,btHeightfieldTerrainShape去创建一些网格图形,首先了解btHeightfieldTerrainShape,通过高度图数据创建一个3D地形。
Astaticmeshthatisoptimisedforanddescribedbythesurfaceofaheightmap.
建议先阅读官网介绍
首先可以下几个效果图
根据高度图数据.raw生成的高度地形图
参数设置HeightfieldInfoinfo(128,128,_heightMapData.getBytes(),PHY_UCHAR,1.6f/uData,-1.f,1.f,btVector3(25.f/uData,25.f/uData));
(uData为_heightMapData的最大值)
参数设置HeightfieldInfoinfo(128,mapData,PHY_FLOAT,btVector3(1.f,1.f));
参数设置HeightfieldInfoinfo(128,PHY_SHORT,1.f));
mapData自定义数据0,1的数据
Bullet自带的Demo中的例子
btHeightfieldTerrainShape有两个构造函数,这里分析较复杂的一个
btHeightfieldTerrainShape(
intheightStickWidth,x轴总宽度
intheightStickLength,z轴总长度
比如width=128,length=64则x轴方向为128,z轴方向为64
constvoid*heightfieldData,高度数据
btScalarheightScale,每个字节*heightScale=实际高度
btScalarminHeight,最小高度
btScalarmaxHeight,最大高度
地形原点=(minHeight+maxHeight)*0.5
intupAxis,方向轴取值0=x,1=y,2=z,决定地形的朝向,类似法向量
PHY_ScalarTypeheightDataType,数据格式3种,PHY_SHORT,PHY_FLOAT
boolflipQuadEdges 方形裁剪
);
举个例子
50*50数据
for(inti=0;i<50;++i)
{
for(intj=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,maxHeight=12.f;
最低点正好为-6.f
3.如果为0,maxHeight=3.f;
最低点正好为-1.5f
1.如果为2,maxHeight=6.f;
最低点正好为-4.f
2.如果为2,maxHeight=12.f;
最低点正好为-7.f
3.如果为2,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;
注意:
网格间隔不要过大,过大会出现物体穿过。
自定义数据类型简化参数传递
- structHeightfieldInfo
- {
- intheightStickWidth;
- intheightStickLength;
- void*heightfieldData;
- PHY_ScalarTypehdt;
- btScalarheightScale;
- btScalarminHeight;
- btScalarmaxHeight;
- intupAxis;
- booluseFloatData;
- boolflipQuadEdges;
- btVector3localScaling;
- HeightfieldInfo(intwidth,intlength,void*data,PHY_ScalarTypetype=PHY_SHORT,
- btScalarheiScale=1.f,btScalarminHei=0.f,btScalarmaxHei=1.f,
- constbtVector3&scale=btVector3(1,1,1),intup=1,
- booluseFloat=false,boolfilpQuad=false):
- heightStickWidth(width),heightStickLength(length),heightfieldData(data),
- heightScale(heiScale),minHeight(minHei),maxHeight(maxHei),
- localScaling(scale),upAxis(up),
- hdt(type),useFloatData(useFloat),flipQuadEdges(filpQuad)
- {}
- };
PhysicsWorld3D创建高度地形图
- btRigidBody*PhysicsWorld3D::addHeightfieldTerrain(constHeightfieldInfo&fieldInfo,constbtVector3&position,constPhysicsMaterial3D&material)
- {
- CCAssert(material.mass==0.f,"heightfield'smassmustbe0.");
- btHeightfieldTerrainShape*heightfieldShape=newbtHeightfieldTerrainShape(
- fieldInfo.heightStickWidth,fieldInfo.heightStickLength,fieldInfo.heightfieldData,fieldInfo.heightScale,
- fieldInfo.minHeight,fieldInfo.maxHeight,fieldInfo.upAxis,fieldInfo.hdt,fieldInfo.flipQuadEdges);
- heightfieldShape->setUseDiamondSubdivision(true);//钻石细分矩形方格会出现对角线
- heightfieldShape->setLocalScaling(fieldInfo.localScaling);
- autobody=getBody(heightfieldShape,position,material);
- _world->addRigidBody(body);
- returnbody;
- }
下面来介绍btBvhTriangleMeshShape,通过载入三角网格,实现网格形状的物理模拟
http://bulletphysics.com/Bullet/BulletFull/classbtBvhTriangleMeshShape.html
看看效果
地形能够与模型完美的融合在一起,而且即使半径为0.1的球体也不会穿过地形
使用的shape就是btBvhTriangleMeshShape,构造方法有两个btBvhTriangleMeshShape(
btStridingMeshInterface*meshInterface,//网格接口,存放网格数据
booluseQuantizedAabbCompression,//压缩?只有buildBvh为true才有效
constbtVector3&bvhAabbMin,
constbtVector3&bvhAabbMax,//mesh不可超过bvhaabb包围盒,只有buildBvh为true才有效
boolbuildBvh=true);//优化BVH
btBvhTriangleMeshShape(
btStridingMeshInterface*meshInterface,
booluseQuantizedAabbCompression,
boolbuildBvh=true);
通过导入一个模型的原始三角形数据,就可以建立上图的地形
如何载入模型数据,官网类关系图
提供btTriangleIndexVertexArray,载入网格数据
btTriangleIndexVertexArray(
intnumTriangles,//三角个数
int*triangleIndexBase,//三角形索引数组首地址
inttriangleIndexStride,//每个三角形索引大小=索引类型大小*3
intnumVertices,//顶点个数
btScalar*vertexBase,//顶点数组首地址
intvertexStride); //每个顶点字节=顶点元素*3
既然索引类型为int,就用int
关于原始三角形数据如何得到,
2.利用Blender或者可以导出模型原始三角数据的软件,直接导出数据
关于Blender一款开源的3D建模软件,官网:http://www.blender.org/,自带游戏引擎,物理引擎就是Bullet
导出三角形数据,Blender有个插件专门导出三角形数据文件后缀名为raw,它是文本格式的,
导出时首先要让模型旋转一定角度,坐标系不是opengl的坐标系,cocos2dx采用的就是opengl的坐标系
raw文件格式非常简单:n行,每行9个浮点数据(描述一个三角形),每三个浮点为一个顶点
3.自定义格式
。。。。
来实现数据的载入吧
首先读取raw文件,实现一个简单的PhysicsHelper3D
- #ifndef__PHYSICS_HELPER_3D_H__
- #define__PHYSICS_HELPER_3D_H__
- #include<cocos2d.h>
- USING_NS_CC;
- classPhysicsHelper3D
- {
- public:
- staticstd::vector<float>loadRaw(constchar*fileName);
- staticboolloadRaw(constchar*fileName,std::vector<float>&verts);
- };
- #endif//!__PHYSICS_HELPER_3D_H__
- #include"PhysicsHelper3D.h"
- std::vector<float>PhysicsHelper3D::loadRaw(constchar*fileName)
- {
- std::vector<float>data;
- if(loadRaw(fileName,data))
- {
- returndata;
- }
- returnstd::vector<float>(0);
- }
- boolPhysicsHelper3D::loadRaw(constchar*fileName,std::vector<float>&verts)
- {
- charline[1024];
- floatoneData;
- autorawData=FileUtils::getInstance()->getStringFromFile(fileName);//利用cocos2dx载入文件
- std::stringstreamss,ssLine;
- ss<<rawData;
- while(ss.getline(line,1024))//读取一行
- {
- ssLine<<line;
- for(inti=0;i<9;i++)//获取9个浮点数
- {
- ssLine>>oneData;
- verts.push_back(oneData);
- }
- }
- returntrue;
- }
并不是很难吧,载入文件办法不好,不过先将就着用吧
- _indexVertexArrays=newbtTriangleIndexVertexArray(_verts.size()/9,&_verIndices[0],3*sizeof(int),
- _verts.size()/3,(btScalar*)&_verts[0],3*sizeof(float));
- _meshShape=newbtBvhTriangleMeshShape(_indexVertexArrays,true);
- _verts是vector<float>三角形的个数=_verts.size()/9
- 为了构建方便实现PhysicsMesh3D
- #ifndef__PHYSICS_MESH_3D_H__
- #define__PHYSICS_MESH_3D_H__
- #include"Bullet/btBulletDynamicsCommon.h"
- #include"cocos2d.h"
- USING_NS_CC;
- classPhysicsMesh3D
- {
- public:
- staticPhysicsMesh3D*constuct(constchar*fileName);
- voiddestroy();
- boolinitWithFile(constchar*fileName);
- private:
- std::vector<float>_verts;//存放顶点
- std::vector<int>_verIndices;//顶点索引
- btTriangleIndexVertexArray*_indexVertexArrays;//三角形数据
- CC_SYNTHESIZE_READONLY(btBvhTriangleMeshShape*,_meshShape,MeshShape);//shape
- };
- #endif
- CC_SYNTHESIZE_READONLY为cocos2dx提供的宏
- boolPhysicsMesh3D::initWithFile(constchar*fileName)
- {
- _indexVertexArrays=nullptr;
- _verts.clear();
- _verIndices.clear();
- if(PhysicsHelper3D::loadRaw(fileName,_verts))//载入数据
- {
- _verIndices.resize(_verts.size());//顶点的位置就是索引
- for(inti=0;i<_verts.size();++i)
- {
- _verIndices[i]=i;
- }
- _indexVertexArrays=newbtTriangleIndexVertexArray(
- _verts.size()/9,//三角形个数
- &_verIndices[0],//三角数据数组首地址
- 3*sizeof(int),//一个三角索引大小
- _verts.size()/3,//顶点个数
- (btScalar*)&_verts[0],//顶点数组首地址
- 3*sizeof(float));//一个顶点大小
- //获取shape
- _meshShape=newbtBvhTriangleMeshShape(_indexVertexArrays,true);
- returntrue;
- }
- returnfalse;
- }
释放申请的内存
- voidPhysicsMesh3D::destroy()
- {
- _verts.clear();
- _verIndices.clear();
- delete_indexVertexArrays;
- deletethis;
- }
- btRigidBody*addTriangleMesh(PhysicsMesh3D*mesh3D,
- constPhysicsMaterial3D&material=PHYSICS_MATERIAL3D_PLANE);
- btRigidBody*PhysicsWorld3D::addTriangleMeshShape(PhysicsMesh3D*mesh3D,constbtVector3&position,constPhysicsMaterial3D&material)
- {
- CCAssert(material.mass==0.f,"body'smassmustbe0.");
- autobody=getBody(mesh3D->getMeshShape(),material);
- _world->addRigidBody(body);
- returnbody;
- }
测试
HelloWorld添加变量
- PhysicsMesh3D*_phyMesh3D;//meshshape
- _phyMesh3D=PhysicsMesh3D::constuct("heightmap.raw");
- _world->addTriangleMesh(_phyMesh3D,btVector3(0,0));
- //载入plane模型
- autospPlane=Sprite3D::create("model/heightmap.c3b");
- this->addChild(spPlane);
- spPlane->setPosition3D(Vec3(0,0));
- spPlane->setRotation3D(Vec3(0,180,0));
onExit()不要忘了
- _phyMesh3D->destroy();
为了方便测试,实现了一个漫游摄像机,有空讲解一下。