Bullet(Cocos2dx)之创建地形
Bullet提供了几个类btBvhTriangleMeshShape,btHeightfieldTerrainShape去创建一些网格图形,首先了解btHeightfieldTerrainShape,通过高度图数据创建一个3D地形。 Astaticmeshthatisoptimisedforanddescribedbythesurfaceofaheightmap. 官网解释:http://bulletphysics.com/Bullet/BulletFull/classbtHeightfieldTerrainShape.html#a90d823ba5f44871a0bcfce0174177223 建议先阅读官网介绍 首先可以下几个效果图
根据高度图数据.raw生成的高度地形图 参数设置HeightfieldInfoinfo(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) 参数设置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; 注意: 网格间隔不要过大,过大会出现物体穿过。
自定义数据类型简化参数传递 struct HeightfieldInfo { int heightStickWidth; int heightStickLength; void* heightfieldData; PHY_ScalarType hdt; btScalar heightScale; btScalar minHeight; btScalar maxHeight; int upAxis; bool useFloatData; bool flipQuadEdges; btVector3 localScaling; HeightfieldInfo(int width,int length,void* data,PHY_ScalarType type = PHY_SHORT,btScalar heiScale = 1.f,btScalar minHei = 0.f,btScalar maxHei = 1.f,const btVector3& scale = btVector3(1,1,1),int up = 1,bool useFloat = false,bool filpQuad = 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(const HeightfieldInfo& fieldInfo,const btVector3& position,const PhysicsMaterial3D& material) { CCAssert(material.mass == 0.f,"height field's mass must be 0."); btHeightfieldTerrainShape* heightfieldShape = new btHeightfieldTerrainShape( 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); auto body = getBody(heightfieldShape,position,material); _world->addRigidBody(body); return body; } 下面来介绍btBvhTriangleMeshShape,通过载入三角网格,实现网格形状的物理模拟 http://bulletphysics.com/Bullet/BulletFull/classbtBvhTriangleMeshShape.html 看看效果 地形能够与模型完美的融合在一起,而且即使半径为0.1的球体也不会穿过地形 使用的shape就是btBvhTriangleMeshShape,构造方法有两个btBvhTriangleMeshShape( btStridingMeshInterface*meshInterface,//网格接口,存放网格数据 booluseQuantizedAabbCompression,//压缩?只有buildBvh为true才有效 constbtVector3&bvhAabbMin, btVector3&bvhAabbMax,//mesh不可超过bvhaabb包围盒,只有buildBvh为true才有效 boolbuildBvh=true);//优化BVH
btBvhTriangleMeshShape( 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 文件 导出三角形数据,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; class PhysicsHelper3D { public: static std::vector<float> loadRaw(const char* fileName); static bool loadRaw(const char* fileName,std::vector<float>& verts); }; #endif // !__PHYSICS_HELPER_3D_H__ #include "PhysicsHelper3D.h" std::vector<float> PhysicsHelper3D::loadRaw(const char* fileName) { std::vector<float> data; if (loadRaw(fileName,data)) { return data; } return std::vector<float>(0); } bool PhysicsHelper3D::loadRaw(const char* fileName,std::vector<float>& verts) { char line[1024]; float oneData; auto rawData = FileUtils::getInstance()->getStringFromFile(fileName); // 利用cocos2dx载入文件 std::stringstream ss,ssLine; ss << rawData; while (ss.getline(line,1024)) // 读取一行 { ssLine << line; for (int i = 0; i < 9; i++) // 获取9个浮点数 { ssLine >> oneData; verts.push_back(oneData); } } return true; } 并不是很难吧,载入文件办法不好,不过先将就着用吧 _indexVertexArrays = new btTriangleIndexVertexArray(_verts.size() / 9,&_verIndices[0],3 * sizeof(int),_verts.size() / 3,(btScalar*)&_verts[0],3 * sizeof(float)); _meshShape = new btBvhTriangleMeshShape(_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; class PhysicsMesh3D { public: static PhysicsMesh3D* constuct(const char* fileName); void destroy(); bool initWithFile(const char* fileName); private: std::vector<float> _verts; // 存放顶点 std::vector<int> _verIndices; // 顶点索引 btTriangleIndexVertexArray* _indexVertexArrays; // 三角形数据 CC_SYNTHESIZE_READONLY(btBvhTriangleMeshShape*,_meshShape,MeshShape); // shape }; #endif CC_SYNTHESIZE_READONLY 为cocos2dx提供的宏 bool PhysicsMesh3D::initWithFile(const char* fileName) { _indexVertexArrays = nullptr; _verts.clear(); _verIndices.clear(); if (PhysicsHelper3D::loadRaw(fileName,_verts)) // 载入数据 { _verIndices.resize(_verts.size()); // 顶点的位置就是索引 for (int i=0; i < _verts.size(); ++i) { _verIndices[i] = i; } _indexVertexArrays = new btTriangleIndexVertexArray( _verts.size() / 9,// 三角形个数 &_verIndices[0],// 三角数据数组首地址 3 * sizeof(int),// 一个三角索引大小 _verts.size() / 3,// 顶点个数 (btScalar*)&_verts[0],// 顶点数组首地址 3 * sizeof(float)); // 一个顶点大小 // 获取shape _meshShape = new btBvhTriangleMeshShape(_indexVertexArrays,true); return true; } return false; } 释放申请的内存 void PhysicsMesh3D::destroy() { _verts.clear(); _verIndices.clear(); delete _indexVertexArrays; delete this; } 在PhysicsWorld3D建立一个添加Mesh的方法 btRigidBody* addTriangleMesh(PhysicsMesh3D* mesh3D,const PhysicsMaterial3D& material = PHYSICS_MATERIAL3D_PLANE); btRigidBody* PhysicsWorld3D::addTriangleMeshShape(PhysicsMesh3D* mesh3D,"body's mass must be 0."); auto body = getBody(mesh3D->getMeshShape(),material); _world->addRigidBody(body); return body; } 测试 HelloWorld添加变量 PhysicsMesh3D* _phyMesh3D; // mesh shape添加网格 _phyMesh3D = PhysicsMesh3D::constuct("heightmap.raw"); _world->addTriangleMesh(_phyMesh3D,btVector3(0,0)); // 载入plane模型 auto spPlane = Sprite3D::create("model/heightmap.c3b"); this->addChild(spPlane); spPlane->setPosition3D(Vec3(0,0)); spPlane->setRotation3D(Vec3(0,180,0)); onExit()不要忘了 _phyMesh3D->destroy();
为了方便测试,实现了一个漫游摄像机,有空讲解一下。 完整源码及资源 github (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |