Bullet(Cocos2dx)之创建地形
Bullet提供了几个类btBvhTriangleMeshShape,btHeightfieldTerrainShape去创建一些网格图形,首先了解btHeightfieldTerrainShape,通过高度图数据创建一个3D地形。@H_404_30@
Astaticmeshthatisoptimisedforanddescribedbythesurfaceofaheightmap.@H_404_30@
官网解释:http://bulletphysics.com/Bullet/BulletFull/classbtHeightfieldTerrainShape.html#a90d823ba5f44871a0bcfce0174177223@H_404_30@
建议先阅读官网介绍@H_404_30@
@H_404_30@
参数设置HeightfieldInfoinfo(128,128,_heightMapData.getBytes(),PHY_UCHAR,1.6f/uData,-1.f,1.f,btVector3(25.f/uData,25.f/uData));@H_404_30@
(uData为_heightMapData的最大值)@H_404_30@
@H_404_30@
自定义数据生成高度地形图(PHY_FLOAT)@H_404_30@
参数设置HeightfieldInfoinfo(128,mapData,PHY_FLOAT,btVector3(1.f,1.f));@H_404_30@
mapData自定义数据,随机0~1的数据@H_404_30@
@H_404_30@
自定义数据生成高度地形图(PHY_FLOAT)@H_404_30@
参数设置HeightfieldInfoinfo(128,PHY_SHORT,1.f));@H_404_30@
btHeightfieldTerrainShape有两个构造函数,这里分析较复杂的一个@H_404_30@
btHeightfieldTerrainShape(@H_404_30@
intheightStickWidth,x轴总宽度@H_404_30@
intheightStickLength,z轴总长度@H_404_30@
比如width=128,length=64则x轴方向为128,z轴方向为64@H_404_30@
constvoid*heightfieldData,高度数据@H_404_30@
btScalarheightScale,每个字节*heightScale=实际高度@H_404_30@
btScalarminHeight,最小高度@H_404_30@
btScalarmaxHeight,最大高度@H_404_30@
地形原点=(minHeight+maxHeight)*0.5@H_404_30@
intupAxis,方向轴取值0=x,1=y,2=z,决定地形的朝向,类似法向量@H_404_30@
PHY_ScalarTypeheightDataType,数据格式3种,PHY_SHORT,PHY_FLOAT@H_404_30@
boolflipQuadEdges 方形裁剪@H_404_30@
);@H_404_30@
举个例子@H_404_30@
50*50数据@H_404_30@
for(inti=0;i<50;++i)@H_404_30@
{@H_404_30@
for(intj=0;j<50;++j)@H_404_30@
{@H_404_30@
heightMap[i*50+j]=j%2;@H_404_30@
}@H_404_30@
}@H_404_30@
对于heightMap[i*50+j]@H_404_30@
1.如果为0,minHeight=0.f,maxHeight=6.f;@H_404_30@
最低点正好为-3.f@H_404_30@
2.如果为0,maxHeight=12.f;@H_404_30@
最低点正好为-6.f@H_404_30@
3.如果为0,maxHeight=3.f;@H_404_30@
最低点正好为-1.5f@H_404_30@
1.如果为2,maxHeight=6.f;@H_404_30@
最低点正好为-4.f@H_404_30@
2.如果为2,maxHeight=12.f;@H_404_30@
最低点正好为-7.f@H_404_30@
3.如果为2,maxHeight=3.f;@H_404_30@
最低点正好为-2.5f@H_404_30@
地形偏移offsetY=-(minHeight+maxHeight);@H_404_30@
不推荐minHeight+maxHeight<0,不稳定@H_404_30@
heightScale*value(heightfieldData[i])为实际高度@H_404_30@
高度计算:@H_404_30@
对于PHY_UCHAR@H_404_30@
最低点y=offsetY+min(heightfieldData);minY=0@H_404_30@
最高点y=offsetY+max(heightfieldData)*heightScale;@H_404_30@
对于PHY_SHORT,PHY_FLOAT@H_404_30@
最高点y=offsetY+max(heightfieldData)*heightScale;@H_404_30@
最低点y=offsetY+min(heightfieldData)*heightScale;@H_404_30@
注意:@H_404_30@
网格间隔不要过大,过大会出现物体穿过。@H_404_30@
@H_404_30@
- 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创建高度地形图@H_404_30@
- 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,通过载入三角网格,实现网格形状的物理模拟@H_404_30@
http://bulletphysics.com/Bullet/BulletFull/classbtBvhTriangleMeshShape.html@H_404_30@
地形能够与模型完美的融合在一起,而且即使半径为0.1的球体也不会穿过地形@H_404_30@ 使用的shape就是btBvhTriangleMeshShape,构造方法有两个
btBvhTriangleMeshShape(@H_404_30@
btStridingMeshInterface*meshInterface,//网格接口,存放网格数据@H_404_30@
booluseQuantizedAabbCompression,//压缩?只有buildBvh为true才有效@H_404_30@
constbtVector3&bvhAabbMin,@H_404_30@
constbtVector3&bvhAabbMax,//mesh不可超过bvhaabb包围盒,只有buildBvh为true才有效@H_404_30@
boolbuildBvh=true);//优化BVH@H_404_30@
@H_404_30@
btBvhTriangleMeshShape(@H_404_30@
btStridingMeshInterface*meshInterface,@H_404_30@
booluseQuantizedAabbCompression,@H_404_30@
boolbuildBvh=true);@H_404_30@
通过导入一个模型的原始三角形数据,就可以建立上图的地形@H_404_30@
如何载入模型数据,官网类关系图@H_404_30@
提供btTriangleIndexVertexArray,载入网格数据@H_404_30@
btTriangleIndexVertexArray(@H_404_30@
intnumTriangles,//三角个数@H_404_30@
int*triangleIndexBase,//三角形索引数组首地址@H_404_30@
inttriangleIndexStride,//每个三角形索引大小=索引类型大小*3@H_404_30@
intnumVertices,//顶点个数@H_404_30@
btScalar*vertexBase,//顶点数组首地址@H_404_30@
intvertexStride); //每个顶点字节=顶点元素*3@H_404_30@
既然索引类型为int,就用int@H_404_30@
关于原始三角形数据如何得到,@H_404_30@
1.可以利用cocos2dx的载入模型函数获取(有待实验)@H_404_30@
2.利用Blender或者可以导出模型原始三角数据的软件,直接导出数据@H_404_30@
关于Blender一款开源的3D建模软件,官网:@L_502_20@,自带游戏引擎,物理引擎就是Bullet@H_404_30@
导出三角形数据,Blender有个插件专门导出三角形数据文件后缀名为raw,它是文本格式的,@H_404_30@
导出时首先要让模型旋转一定角度,坐标系不是opengl的坐标系,cocos2dx采用的就是opengl的坐标系@H_404_30@
raw文件格式非常简单:n行,每行9个浮点数据(描述一个三角形),每三个浮点为一个顶点@H_404_30@
。。。。@H_404_30@
来实现数据的载入吧@H_404_30@
首先读取raw文件,实现一个简单的PhysicsHelper3D@H_404_30@
- #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;
- }
并不是很难吧,载入文件办法不好,不过先将就着用吧@H_404_30@
- _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;
- }
释放申请的内存@H_404_30@
- voidPhysicsMesh3D::destroy()
- {
- _verts.clear();
- _verIndices.clear();
- delete_indexVertexArrays;
- deletethis;
- }
在PhysicsWorld3D建立一个添加Mesh的方法@H_404_30@
- 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;
- }
测试@H_404_30@
- 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()不要忘了@H_404_30@
- _phyMesh3D->destroy();
@H_404_30@
为了方便测试,实现了一个漫游摄像机,有空讲解一下。@H_404_30@