Bullet(Cocos2dx)之创建地形

Bullet提供了几个类btBvhTriangleMeshShape,btHeightfieldTerrainShape去创建一些网格图形,首先了解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;

注意:

网格间隔不要过大,过大会出现物体穿过。

自定义数据类型简化参数传递

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,  // 网格接口,存放网格数据

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

#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* addTriangleMeshShape(PhysicsMesh3D* mesh3D, const btVector3& position,
const PhysicsMaterial3D& material = PHYSICS_MATERIAL3D_PLANE);

btRigidBody* PhysicsWorld3D::addTriangleMeshShape(PhysicsMesh3D* mesh3D, const btVector3& position, const PhysicsMaterial3D& material)
{
	CCAssert(material.mass == 0.f, "body's mass must be 0.");

	auto body = getBody(mesh3D->getMeshShape(), position, material);
	_world->addRigidBody(body);

	return body;
}

测试

HelloWorld 添加变量

PhysicsMesh3D* _phyMesh3D;	// mesh shape

添加网格

_phyMesh3D = PhysicsMesh3D::constuct("heightmap.raw");
_world->addTriangleMeshShape(_phyMesh3D, btVector3(0, 0, 0));

// 载入plane模型
auto spPlane = Sprite3D::create("model/heightmap.c3b");
this->addChild(spPlane);
spPlane->setPosition3D(Vec3(0, 0, 0));
spPlane->setRotation3D(Vec3(0, 180, 0));

onExit()不要忘了

_phyMesh3D->destroy();

为了方便测试, 实现了一个漫游摄像机,有空讲解一下。

完整源码及资源

github

时间: 2024-10-13 00:28:11

Bullet(Cocos2dx)之创建地形的相关文章

Bullet(Cocos2dx)之凸多面体形状和组合形状

接高度地形图和三角形网格,同样是复杂图形的创建. 先来看看效果 左边通过提供的顶点生成的多面体,右边创建一个由球体与长方体组成的组合形状 首先来了解凸多面体 根据提供的顶点创建一个凸多面体形状,无论给定的顶点顺序是怎样的,都是创建一个由这些顶点组成的凸多面体. 先来看看btConvexHullShape的解释及构造函数 ///this constructor optionally takes in a pointer to points. Each point is assumed to be

将cocos2dx+lua创建的游戏port到windows phone

在整个Port的过程中遇到的问题总结如下 1.一定要使用最新版本的cocos2dx,原因大家看一下changelog就知道了,最近的cocos2dx版本都是在修windows phone上的bug,所以为了避免少出问题,还是直接升级到最新版本吧 2.如果你使用的是cocos2dx + lua方式,目前的project-creator并不支持lua版本的windows phone平台,但是cpp版本是支持的,因此我们可以在cpp版本的基础上把libcocoslua以及liblua两个工程加到项目中

Cocos2d-X中创建菜单项

Cocos2d-X中创建菜单的类: CCMenuItemFont:创建纯文本的菜单项 CCMenuItemAtlasFont:创建带有艺术字体的菜单项 CCMenuItemImage:用图片创建菜单项 CCMenuItemLabel:用标签创建菜单项 CCMenuItemSprite:用精灵创建菜单项 CCMenuItemToggle:创建可以来回切换的菜单项如ON/OFF 程序实例:创建一个纯文本菜单项 程序代码: #include "MenuItem.h" CCScene* Men

Unity3D动态创建地形网格(一)

最近一直都在搞flash3D,好像有点对不起Unity3D的朋友们了.这次简单的写一个动态创建地形网格的脚本给大家分享一下. 这次是第一部分,仅仅实现了通过高度图动态生成地形的部分.假如以后有心情和时间,再来慢慢的补充多通道刷地形材质.动态刷地形和保存高度图等的功能吧.以前我都不喜欢公开脚本源码,都是一个个部分的单独讲解然后让朋友们自己去组合起来的,但最近时间实在是不多,所以还是直接提供源码,然后在源码上面写注释,大家自行的观看吧.源码在最下面. 首先直接把脚本拖到某物体上面,运行,就会出现了上

Cocos2d-x项目创建

以创建HelloWorld项目为例子,执行create_project.py脚本,进入Doc界面输入下面的命令: (1)E:   (切换盘符,因为我的Cocos2d-x源码在E盘,create_project.py在该目录下.) (2)cd E:\\Cocos2D\\cocos2d-x-2.2.2\\tools\\project-creator   (进入到create_project.py脚本所在目录) (3)下载python,下载地址:http://www.python.org/getit/

TerraBuilder创建地形之去除影像黑边,填充影像

最近在Skyline项目中使用TerraBuilder创建地形,由于地形比较大,分块下载卫星影像,然后再TerraBuilder中合并,由于合并.图形等等问题,导致创建处理出来的地形中存在严重的缝隙和黑边. 工具: TerraBuilder,TerraExplorer Pro(均为6.6.1版本) 方法1: 通过修改卫星影像的在TerraBuilder中的添加顺序来去除多个影像交界处的黑边(必须是某些下载的卫星影像中不存在黑边才行) 1)块1卫星影像的上边界存在黑边问题   2)与之相邻(相交界

Bullet(Cocos2dx)之分析刚体创建与销毁(Primitives)

相关链接:http://bulletphysics.org/mediawiki-1.5.8/index.php/Collision_Shapes Bullet基本图形简介 Bullet提供的基本图形包括球体.长方体.圆柱体.胶囊体.圆锥体.多球体 当然还有一个Plane,一个无限的平面 1.球体是个很简单的形状: btSphereShape (btScalar radius) 提供一个球体的半径 2.长方体(盒子) btBoxShape (const btVector3 &boxHalfExte

Bullet(cocos2dx)学习制作桌球游戏之前期准备

使用cocos2dx结合bullet设计一款简陋的桌球游戏,就是为了回顾前期学过的bullet. 首先要把桌球游戏需要的基本资源准备好,15个球,1个白球,1张台球桌,球杆可有可无. 先看看目前实现的效果 至于这张台球桌的模型,我是随便设计一下 当然这个模型只是为了加载raw文件(静态网格数据),为了使模型的贴图显示出来, 我使用Blender直接创建了一个Plane,然后将台球桌的贴图贴在Plane上,于是就能以假乱真的 实现一个台球桌 在游戏开始前,初始化物理环境,加载一张台球桌,设置相应的

1 游戏逻辑架构,Cocos2d-x游戏项目创建,HelloWorld项目创建,HelloWorld程序分析,(CCApplicationProtocol,CCApplication,AppDeleg

 1 游戏逻辑架构 详细介绍 A 一个导演同一时间只能运行一个场景,场景当中,可以同时加载多个层,一个层可以可载多个精灵.层中亦可以加层. B  场景切换 sceneàaddChild(layer); layeràaddChild(sprite); 2 项目创建命令: A 进入tools下的project-creat E:\Installed\cocos2d-x-2.2.3\tools\project-creator> B python create_project.py -project