Bullet提供了几个类btBvhTriangleMeshShape,btHeightfIEldTerrainShape去创建一些网格图形,首先了解btHeightfIEldTerrainShape,通过高度图数据创建一个3D地形。
Astaticmeshthatisoptimisedforanddescribedbythesurfaceofaheightmap.
官网解释:http://bulletphysics.com/Bullet/BulletFull/classbtHeightfieldTerrainShape.html#a90d823ba5f44871a0bcfce0174177223
建议先阅读官网介绍
首先可以下几个效果图
根据高度图数据.raw生成的高度地形图
参数设置HeightfIEldInfoinfo(128,128,_heightmapData.getBytes(),PHY_UCHAR,1.6f/uData,-1.f,1.f,btVector3(25.f/uData,25.f/uData));
(uData为_heightmapData的最大值)
自定义数据生成高度地形图(PHY_float)
参数设置HeightfIEldInfoinfo(128,mapData,PHY_float,btVector3(1.f,1.f));
mapData自定义数据,随机0~1的数据
自定义数据生成高度地形图(PHY_float)
参数设置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;
注意:
网格间隔不要过大,过大会出现物体穿过。
自定义数据类型简化参数传递
[cpp] view plain copy 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创建高度地形图
[cpp] view plain copy 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
关于原始三角形数据如何得到,
1.可以利用cocos2dx的载入模型函数获取(有待实验)
2.利用Blender或者可以导出模型原始三角数据的软件,直接导出数据
关于Blender一款开源的3D建模软件,官网:http://www.blender.org/,自带游戏引擎,物理引擎就是Bullet
导出三角形数据,Blender有个插件专门导出三角形数据文件后缀名为raw,它是文本格式的,
导出时首先要让模型旋转一定角度,坐标系不是opengl的坐标系,cocos2dx采用的就是opengl的坐标系
raw文件格式非常简单:n行,每行9个浮点数据(描述一个三角形),每三个浮点为一个顶点
3.自定义格式
。。。。
来实现数据的载入吧
首先读取raw文件,实现一个简单的PhysicsHelper3D
[cpp] view plain copy #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; }并不是很难吧,载入文件办法不好,不过先将就着用吧
[cpp] view plain copy _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; }释放申请的内存
[cpp] view plain copy voIDPhysicsMesh3D::destroy() { _verts.clear(); _verIndices.clear(); delete_indexVertexArrays; deletethis; }在PhysicsWorld3D建立一个添加Mesh的方法
[cpp] view plain copy 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添加变量
[cpp] view plain copy PhysicsMesh3D*_phyMesh3D;//meshshape 添加网格 [cpp] view plain copy _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()不要忘了
[cpp] view plain copy _phyMesh3D->destroy();
为了方便测试,实现了一个漫游摄像机,有空讲解一下。
完整源码及资源
github
总结以上是内存溢出为你收集整理的Bullet(Cocos2dx)之创建地形全部内容,希望文章能够帮你解决Bullet(Cocos2dx)之创建地形所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)