bullet学习日记
最近需要bullet用物理引擎做一个测量类的项目,因为半途接手,物理部分其实已经实现,但犹于对bullet基本不了解,导致相关部分完全改不动,这两天静下心来把物理引擎用法了解了一翻,顺便做点笔记,以便以后回头看rep,提高类似工作的效率。
因为英文水平并不高,基本上搜索中文资料,然鹅真正专业的都是英文,并且看不太懂。两种学习方式里,感觉自己比较适合从上端往下学习的方式(即直接从demo代码开始了解其功能,一点一点了解。另外一种从下端往上端学习的方式是从文档入手,看完再开始看demo写代码)。直接看文档,太过抽象。
一、
一开始我对物理引擎的实现没有认识,在心中构思物理引擎的实现方式,主要有两种方式:一种是他有一个类,我们继承它,便具备了物理特性,一种是它是一个库,我们使用他提供的方法来计算我们的数据。
然而开始了解之后,才发现比较接近第二种想法。真实的是他通过宏选择模块,项目中引用相关文件,然后便可以实例相关类,并使用相关方法。
----->
这一部分是在我对使用Bullet有了应用上的认识之后写的:
bullet像很多仿真系统一样,
先需要你按它的要求但创建一个仿真环境,包括地百等;
然后向其中添加你要仿真的对象以及其属性,如电路仿真是电路元件模型,而这里则是刚体,软体等模型;
然后便可以开始仿真了,即调用它提供的开始接口,参数通常会是一个时间,他返回该时间后对象的状态,即运算结果。
通过对bullet的了解过程,这里做个小总结,基本上从了解如何创建仿真环境开始,稍后是如何向其中添加仿真元素,最后是他的交互过程,即如何仿真并向用户反馈结果。
但是在应用时,我希望能直接使用他内部的检测算法,或者中间产物,而不直接使用的运算结果,因为我并非需要他的结果,而是只需要它帮助计算碰撞是否已习惯以发生,或者发生碰撞的位置等。所以还需要继续了解,以使能灵活的使用大牛的资源。20190814
<-----
二、
具体基本流程如下:这是网上找到的一篇较为清晰易懂的适合初学者的文章,希望对同是初识者的你有所帮助。
Bullet教程: Hello World 实例
更多信息请关注 物理引擎中文社区http://www.physicsengine.org
这篇文章里我们将尽可能简单的向你展示怎么使用Bullet, 怎样初始化Bullet, 设置一个动力学世界, 还有一个球落向地表 这个对鉴别你的build是否成功非常有用并且也能够让你快速的学习到Bullet的API. 首先,我们假设你的Bullet已经正确安装并且正确设置了Bullet的include路径(例如. /usr/local/include/bullet) 确保能连接到正确的lib. 否则请参阅Installation安装. 如果你用的是gcc来编译,请确保你的静态库反序,就是说. dynamics, collision, math.
初始化程序
以一个标准的hello world程序开始:
- #include <iostream>
- int main ()
- {
- std::cout << "Hello World!" << std::endl;
- return 0;
- }
创建世界
现在我们要添加一个子弹(Bullet)模拟. 首先写入以下语句:
#include <btBulletDynamicsCommon.h>
我们想把btDiscreteDynamicsWorld 实例化但是在做此之前我们还需要解决一些其他事情. 对于一个“hello world”例子来说它太复杂我们并不需要. 但是,为了能更符合我们自己的工程, 他们可以用来微调(fine-tuning)模拟环境.
我们需要指出使用什么样的 Broadphase algorithm(宽相算法). 选择什么样的泛型算法很总要,如果有许多刚体在绘制场景里, since it has to somehow check every pair which when implemented naively(天真) is an O(n^2) problem.
宽相(broadphase)使用 传统的近似物体形状并且被称之为代理.我们需要提前告诉子弹最大的代理数, 所以才能很好的分配内存避免浪费. 下面就是世界里任何时候的最大刚体数.
int maxProxies = 1024;
一些 broadphases 使用特殊的结构要求世界的尺度提前被告知, 就像我们现在遇到的情况一样. 该broadphase可能开始严重故障,如果离开这个物体体积. 因为 the AxisSweep broadphase quantizes 空间基于我们使用的整个空间的大小, 您想这差不多等于你的世界.
使它小于你的世界将导致重大问题, 使它大于你的世界将导致低劣的性能.这是你程序调整的一个简单部分, 所以为了确保数字的正确多花点时间也不防.
在这个例子中,世界从起点开始延伸10公里远。
- btVector3 worldAabbMin(-10000,-10000,-10000);
- btVector3 worldAabbMax(10000,10000,10000);
这个broadphase是我们将要使用的, 这个执行的是扫描和裁剪, 这里可以看到更多解释Broadphase .
btAxisSweep3* broadphase = new btAxisSweep3(worldAabbMin,worldAabbMax,maxProxies);
该broadphase是一个极好的空间以消除不应碰撞的成队物体. 这是为了提高运行效率.
您可以使用碰撞调度注册一个回调,过滤器重置broadphase代理,使碰撞系统不处理系统的其它无用部分
. 更多信息请看 Collision Things.
碰撞配置可以让你微调算法用于全部(而不是不是broadphase )碰撞检测。这个方面现在还属于研究阶段
- btDefaultCollisionConfiguration* collisionConfiguration = new btDefaultCollisionConfiguration();
- btCollisionDispatcher* dispatcher = new btCollisionDispatcher(collisionConfiguration);
我们还需要一个"solver". 这是什么原因导致物体进行互动得当,考虑到重力,游戏逻辑等的影响,碰撞,会被制约。
它工作的很好,只要你不把它推向极端,对于在任何高性能仿真都有瓶颈有一些相似的可以线程模型:
btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver;
终于我们可以初始化了世界了:
btDiscreteDynamicsWorld* dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration);
很明显我们把重力方向设置成了Y轴的负方向,即Y轴是像上的
dynamicsWorld->setGravity(btVector3(0,-10,0));
子弹的政策是“谁分配,也删除” 记住,必须符合这样的结果
在main()后记的删除.
我们提供了一个通用的结果. 代码如下:
- #include <btBulletDynamicsCommon.h>
- #include <iostream>
- int main () {
- std::cout << "Hello World!" << std::endl;
- // Build the broadphase
- int maxProxies = 1024;
- btVector3 worldAabbMin(-10000,-10000,-10000);
- btVector3 worldAabbMax(10000,10000,10000);
- btAxisSweep3* broadphase = new btAxisSweep3(worldAabbMin,worldAabbMax,maxProxies);
- // 设置好碰撞属性 和调度
- btDefaultCollisionConfiguration* collisionConfiguration = new btDefaultCollisionConfiguration();
- btCollisionDispatcher* dispatcher = new btCollisionDispatcher(collisionConfiguration);
- // 实际上的物理模拟器
- btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver;
- // 世界.
- btDiscreteDynamicsWorld* dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration);
- // 这里做一些你想做的事
- // 作为一个好的编程习惯 做好删除工作
- delete dynamicsWorld;
- delete solver;
- delete dispatcher;
- delete collisionConfiguration;
- delete broadphase;
- return 0;
- }
碰撞包围体
我们将创造一个接地平面[静态刚体] ,和一个球体,将属于在地上[动态刚体] 。每个刚体需要参考碰撞包围体. 碰撞包围体只解决碰撞检测问题, 因此没有质量,惯性,恢复原状等概念. 如果您有许多代理,使用相同的碰撞形状[例如每飞船模拟是一个5单元半径范围]。这是个好做法,只有一个子弹形状的碰撞,并分享它在所有这些代理. 但是我们这里的两个刚体形状都不一样,所以他们需要各自的shape.
地面通常是向上的并且里原始点1米的样子. 地面会和远点交叉,但子弹不允许这样做,
因此,我们将抵消它的1米和用来弥补,当我们把刚体设置好以后。
btCollisionShape* groundShape = new btStaticPlaneShape(btVector3(0,1,0),1);
我们将让它从天上掉下来,它是一个球体,半径为1米.
btCollisionShape* fallShape = new btSphereShape(1);
这里需要做碰撞形状的清理工作.
刚体
在,我们可以添加形状的碰撞到我们的现场,并将它们定位.
让我们先初始化地面. 它的方向是特定的, 子弹的四元数形式 x,y,z,w . 位置在地面下一米, 将要补充一米我们不得不做的. 运动状态在这里可以得到详细的说明:MotionStates
- btDefaultMotionState* groundMotionState =
- new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,-1,0)));
在第一个和最后一个参数,在下面的构造函数中是质量和地表的惯性. 由于地面是静止的所以我们把它设置成0. 固定不动的物体,质量为0 -他是固定的.
- btRigidBody::btRigidBodyConstructionInfo
- groundRigidBodyCI(0,groundMotionState,groundShape,btVector3(0,0,0));
- btRigidBody* groundRigidBody = new btRigidBody(groundRigidBodyCI);
最后我们把地面加到世界中:
dynamicsWorld->addRigidBody(groundRigidBody);
新增下跌领域非常相似。我们将其置于50米以上的地面.
btDefaultMotionState* fallMotionState = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,50,0)));
由于它是动态刚体,我们将给予质量1公斤。我不记得如何计算一个球体的惯性,但是,这并不重要,因为子弹提供它的实现
- btScalar mass = 1;
- btVector3 fallInertia(0,0,0);
- fallShape->calculateLocalInertia(mass,fallInertia);
现在,我们可以建造刚体只是像以前一样,并把它加到世界中:
- btRigidBody::btRigidBodyConstructionInfo fallRigidBodyCI(mass,fallMotionState,fallShape,fallInertia);
- btRigidBody* fallRigidBody = new btRigidBody(fallRigidBodyCI);
- dynamicsWorld->addRigidBody(fallRigidBody);
一个快速的解释btRigidBody::btRigidBodyConstructionInfo是为了; 物体的构建是通过某些参数的. 这是通过一个特殊的结构实现的。 该部分的btRigidBodyConstructionInfo被复制到物体当你建造的时候,并只用于在初始化的时候. 如果你想创建几千个属性一样的物体, 你只需要建立一个btRigidBodyConstructionInfo, 并通过它创建所有的.
开始模拟
这就是有趣的开始。我们会加强模拟200倍,间隔60赫兹. 这使它有足够的时间降落的地面上. 每一步, 我们都会打印出它离地面的高度.
这stepSimulation 在做你所期待, 不过他的接口确实很复杂. 读Stepping The World 以获得更多消息.
进后,我们审查的状态下降领域.位置和方向都封装在btTranform对象,我们摘录下降领域的运动状态. 我们只关心位置,我们退出变换getOrigin ( ) 。然后,我们打印y组成部分的立场载体.
|
这应该产生一个输出看起来像这样的东西:
sphere height: 49.9917
sphere height: 49.9833
sphere height: 49.9722
sphere height: 49.9583
sphere height: 49.9417
sphere height: 49.9222
sphere height: 49.9
...
sphere height: 1
sphere height: 1
sphere height: 1
sphere height: 1
sphere height: 1
看起来不错迄今。如果你图这对输出迭代次数,你就会得到这个:
这个球体开始于地表的一米处. 这是因为取的是几何中心并且它的半径为1米. 这个球刚开始会有一个大的反弹然后渐渐的减缓弹起高度.
这是可以预料的实时物理引擎,但它可以尽量减少,增加频率的模拟步骤
. 试试再说!
现在你可以把这个动态世界代入你的程序 实时绘制出这个球体. 也可以看看其他的 Collision Shapes . 试试一堆盒子 或者圆柱体然后用一个球去扔向他们.
完整代码
- #include <iostream>
- #include <btBulletDynamicsCommon.h>
- int main (void)
- {
- btVector3 worldAabbMin(-10000,-10000,-10000);
- btVector3 worldAabbMax(10000,10000,10000);
- int maxProxies = 1024;
- btAxisSweep3* broadphase = new btAxisSweep3(worldAabbMin,worldAabbMax,maxProxies);
- btDefaultCollisionConfiguration* collisionConfiguration = new btDefaultCollisionConfiguration();
- btCollisionDispatcher* dispatcher = new btCollisionDispatcher(collisionConfiguration);
- btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver;
- btDiscreteDynamicsWorld* dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration);
- dynamicsWorld->setGravity(btVector3(0,-10,0));
- btCollisionShape* groundShape = new btStaticPlaneShape(btVector3(0,1,0),1);
- btCollisionShape* fallShape = new btSphereShape(1);
- btDefaultMotionState* groundMotionState = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,-1,0)));
- btRigidBody::btRigidBodyConstructionInfo
- groundRigidBodyCI(0,groundMotionState,groundShape,btVector3(0,0,0));
- btRigidBody* groundRigidBody = new btRigidBody(groundRigidBodyCI);
- dynamicsWorld->addRigidBody(groundRigidBody);
- btDefaultMotionState* fallMotionState =
- new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,50,0)));
- btScalar mass = 1;
- btVector3 fallInertia(0,0,0);
- fallShape->calculateLocalInertia(mass,fallInertia);
- btRigidBody::btRigidBodyConstructionInfo fallRigidBodyCI(mass,fallMotionState,fallShape,fallInertia);
- btRigidBody* fallRigidBody = new btRigidBody(fallRigidBodyCI);
- dynamicsWorld->addRigidBody(fallRigidBody);
- for (int i=0 ; i<300 ; i++) {
- dynamicsWorld->stepSimulation(1/60.f,10);
- btTransform trans;
- fallRigidBody->getMotionState()->getWorldTransform(trans);
- std::cout << "sphere height: " << trans.getOrigin().getY() << std::endl;
- }
- dynamicsWorld->removeRigidBody(fallRigidBody);
- delete fallRigidBody->getMotionState();
- delete fallRigidBody;
- dynamicsWorld->removeRigidBody(groundRigidBody);
- delete groundRigidBody->getMotionState();
- delete groundRigidBody;
- delete fallShape;
- delete groundShape;
- delete dynamicsWorld;
- delete solver;
- delete collisionConfiguration;
- delete dispatcher;
- delete broadphase;
- return 0;
- }
bullet学习日记的更多相关文章
- Linux学习日记-使用EF6 Code First(四)
一.在linux上使用EF 开发环境 VS2013+mono 3.10.0 +EF 6.1.0 先检测一下EF是不是6的 如果不是 请参阅 Linux学习日记-EF6的安装升级(三) 由于我的数据库 ...
- android学习日记05--Activity间的跳转Intent实现
Activity间的跳转 Android中的Activity就是Android应用与用户的接口,所以了解Activity间的跳转还是必要的.在 Android 中,不同的 Activity 实例可能运 ...
- android学习日记03--常用控件Dialog
常用控件 9.Dialog 我们经常会需要在Android界面上弹出一些对话框,比如询问用户或者让用户选择.这些功能我们叫它Android Dialog对话框 对话框,要创建对话框之前首先要创建Bui ...
- android学习日记03--常用控件checkbox/radiobutton
常用控件3.checkbox 复选框,确定是否勾选,点击一下勾选,点击第二下取消,当有一系列备选项时适合用checkbox控件,方便用户提交数据. 贴上例子Activity的java代码 packag ...
- android学习日记03--常用控件button/imagebutton
常用控件 控件是对数据和方法的封装.控件可以有自己的属性和方法.属性是控件数据的简单访问者.方法则是控件的一些简单而可见的功能.所有控件都是继承View类 介绍android原生提供几种常用的控件bu ...
- Zend Framework学习日记(2)--HelloWorld篇(转)
Zend Framework学习日记(2)--HelloWorld篇 这一篇主要演示如何用zf命令行工具建立一个基于Zend Framework框架的工程,也是我初学Zend Framework的小练 ...
- Zend Framework学习日记(1)--环境搭建篇(转)
Zend Framework学习日记(1)--环境搭建篇 (1)开发工具 Zend Framework框架:http://framework.zend.com/download/latest 包含2个 ...
- Python 学习日记(第三周)
知识回顾 在上一周的学习里,我学习了一些学习Python的基础知识下面先简短的回顾一些: 1Python的版本和和安装 Python的版本主要有2.x和3.x两个版本这两个版本在语法等方面有一定的区别 ...
- 配置ssh免密码登录——集群学习日记
度过了难熬的考试月时期之后,最近和小伙伴一起参加的的比赛进入了紧张的准备时期.在进行工作的时候,发现有很多基础的知识点,自己不是很清楚以及了解,所以在想,要不就边学习的时候边写下学习日记,以供自己后来 ...
随机推荐
- hibernate 2 多对多映射
一.实体类 1.Classes.java package cn.gs.wwg.entity; import java.util.Set; public class Classes { private ...
- vue组件之间通信总结---点赞
总结:父组件-->子组件 ①通过属性 步骤1: <son myName="michael" myPhone='123'></son> <son ...
- 【ABAP系列】SAP ABAP SY-SUBRC的含义解析
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP ABAP SY-SUBR ...
- 【MM系列】SAP MM模块-科目分配的配置
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP MM模块-科目分配的配置 ...
- cocos2dx基础篇(14) 滚动视图CCScrollView
[3.x] (1)去掉 "CC" (2)滚动方向 > CCScrollViewDirection 改为强枚举 ScrollView::Dire ...
- 判断RecyclerView是否滚动到底部
转:http://www.jianshu.com/p/c138055af5d2 一.首先,我们来介绍和分析一下第一种方法,也是网上最多人用的方法: public static boolean isVi ...
- Springboot2.x集成单节点Redis
Springboot2.x集成单节点Redis 说明 在Springboot 1.x版本中,默认使用Jedis客户端来操作Redis,而在Springboot 2.x 版本中,默认使用Lettuce客 ...
- [19/05/27-星期一] JavaScript_ 条件语句(if语句)和循环语句(while 、for、do-while)
一.条件语句 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <ti ...
- (3.4)常用知识-char与varchar的选择
1.char与varchar的比较 (1)数据存储开销 [1]varchar列需要2个额外的字节来记录存储数据的长度 [2]每个可为null的char列,需要一些字节(空位图)来反应数据的为空性 [3 ...
- React事件绑定的几种方式对比
React事件绑定 由于类的方法默认不会绑定this,因此在调用的时候如果忘记绑定,this的值将会是undefined.通常如果不是直接调用,应该为方法绑定this.绑定方式有以下几种: 1. 在构 ...