Ammo.js ( Bullet Physics Engine ) - Rigid Body

btCollisionObject

btCollisionObject 继承图

状态

  • ACTIVE_TAG 激活
  • ISLAND_SLEEPING 睡眠
  • WANTS_DEACTIVATION 可被检测
  • DISABLE_DEACTIVATION 无法被检测
  • DISABLE_SIMULATION 无法被模拟

使用 setActivationState() 来设置状态, getActivationState() 获取状态

btRigidBodybtSoftBody 都继承自 btCollisionObject 对象


btRigidBody

下述内容翻译整理自Bullet Physics官方文档—传送门

btRigidBody 是刚体对象的基类,它派生自 btCollisionObject ,因此它保留了指向 btCollisionShape 的指针。注意 Object 与 Shape

class btRigidBody  : public btCollisionObject
{
	...
	btMotionState* m_optionalMotionState;
	...
}

出于性能和内存使用考虑,建议尽可能共享 btCollisionShape 对象(对于用户自定义的不同刚体对象,若它们有相同的碰撞形状,则可以向它们传递该形状的指针,下述第二种构造方式中 btCollisionShape* collisionShape 传递指针)。

Bullet 中的刚体有三种类型:

Dynamic,Static 和 Kinematic Rigid Bodies

  1. 具有正质量的动态刚体(Dynamic RigidBody)。运动由刚体动力学控制由引擎本身改变刚体对象的位置和旋转属性)。
  2. 固定的零质量物体(Static)。它们不会移动(基本上是碰撞对象),如地面和各种墙体等。
  3. 运动学(Kinematic /US [ˌkɪnə’mætɪk]/ )对象,它是没有质量的对象,但用户可以移动它们由鼠标拖拽或键盘直接改变刚体对象的位置和旋转属性)。存在途中交互(on-way interaction),并且Bullet根据时间步长(time step,相邻两次计算的时间间隔)以及之前和当前的世界变换计算速度。

当速度低于给定时间内的阈值时,Bullet 会自动停用动态(Dynamic)刚体。停用(休眠)刚体不会占用任何处理时间,除了轻微的宽相位碰撞检测影响(允许活动对象激活/唤醒休眠对象)

所有这些刚体都需要加入到动态世界(dynamics world)中。可以为刚体指定碰撞形状。这种形状可以用来计算质量分布,也称为惯性矩(或转动惯量,可形式地理解为一个物体对于旋转运动的惯性)。

对于动态对象(dynamic objects),可以使用碰撞形状(collision shape)近似局部惯性矩,否则使用零向量(默认参数),即下述两种构造方式中的第四个参数。

Bullet 中 btRigidBody的两种构造方式
// 用构造信息的btRigidBody构造函数
btRigidBody::btRigidBody(const btRigidBodyConstructionInfo & constructionInfo)	
// btRigidBody构造函数用于向后兼容。
btRigidBody::btRigidBody(	btScalar mass, 
							btMotionState* motionState, 
							btCollisionShape* collisionShape, 
							const btVector3 & localInertia = btVector3(0,0,0) 
						)	

两者虽然看起来有所区别,但在内部的实现基本一致,都会用到 btRigidBodyConstructionInfo

btRigidBody::btRigidBody(const btRigidBody::btRigidBodyConstructionInfo& constructionInfo)
{
	setupRigidBody(constructionInfo);
}

btRigidBody::btRigidBody(btScalar mass, btMotionState* motionState, btCollisionShape* collisionShape, const btVector3& localInertia)
{
	btRigidBodyConstructionInfo cinfo(mass, motionState, collisionShape, localInertia);
	setupRigidBody(cinfo);
}

btRigidBodyConstructionInfo 结构体(structure)提供了用于创建刚体的信息。

将质量设置为零(mass = 0)将创建固定(非动力学)刚体,即上述的第二种刚体对象。

可以使用运动状态(motion state,即 btMotionState )同步物理对象和图形对象之间的世界变换。如果提供了运动状态,则刚体将从运动状态初始化其初始世界变换。


btMotionState

MotionState是什么?

MotionState是一种让Bullet为您完成所有繁重工作的方式,可以将要模拟的对象的世界变换转换到程序的渲染部分。

在大多数情况下,游戏循环会在每帧之前迭代所有要模拟的对象。

对于每个对象,可以从物理实体更新渲染对象的位置。

Bullet使用一种名为MotionState的东西来省去这项工作。

MotionState还有多个其他好处:

  • 移动物体时涉及的计算只对已移动的物体进行;如果渲染对象没有移动,则没有必要每帧更新它的位置。
  • 你不需要只用它们来渲染东西。它们可以有效地通知网络代码,某个实体已经移动,需要在整个网络中进行更新。
  • 插值通常仅在屏幕上可见内容的上下文中才有意义。Bullet 通过 MotionState管理身体插值。
  • 可以跟踪图形对象和重心变换之间的移动。
  • 它们很容易
// btMotionState.h

#ifndef BT_MOTIONSTATE_H
#define BT_MOTIONSTATE_H

#include "btTransform.h"

// btMotionState接口类允许动力学世界使用图形同步和内插更新的世界变换。
// 对于优化,可能只同步移动对象(使用setWorldPosition/setWorldOrientation)
class btMotionState
{
public:
	virtual ~btMotionState()
	{
	}

	virtual void getWorldTransform(btTransform& worldTrans) const = 0;

	//Bullet only calls the update of worldtransform for active objects
	virtual void setWorldTransform(const btTransform& worldTrans) = 0;
};

#endif  //BT_MOTIONSTATE_H

两种方法:

  • btRigidBody::getWorldTransform(btTransform& worldTrans)
  • btRigidBody::setWorldTransform(const btTransform& worldTrans)

相关代码

下述代码描述了使用 Ammo.js 构造 RigidBody 的简单代码:

// 第一刚体的碰撞形状(下面列举的是一个Box)
let shape = new Ammo.btBoxShape(new Ammo.btVector3(2, 2, 2));

// transform 用于描述刚体初始化时的位置和旋转量
let transform = new Ammo.btTransform();
transform.setIdentity();	// 初始化
transform.setOrigin(new Ammo.btVector3(pos.x, pos.y, pos.z));
transform.setRotation(new Ammo.btQuaternion(quat.x, quat.y, quat.z, quat.w));

let motionState = new Ammo.btDefaultMotionState(transform);

let localInertia = new Ammo.btVector3(0, 0, 0);
shape.calculateLocalInertia(mass, localInertia);

let rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, shape, localInertia);

let body = new Ammo.btRigidBody(rbInfo);

// 所有这些刚体都需要加入到动态世界(dynamics world)中。
physicsWorld.addRigidBody(body);

相关链接

Rotation & Inertia Tensors

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用 ammo.js 实现软体模拟。以下是一个简单的例子,展示了如何使用 ammo.js 创建一个软体模拟: ```javascript // 引入 ammo.js import * as Ammo from 'ammo.js'; // 创建 ammo.js 的世界 const collisionConfiguration = new Ammo.btDefaultCollisionConfiguration(); const dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration); const broadphase = new Ammo.btDbvtBroadphase(); const solver = new Ammo.btSequentialImpulseConstraintSolver(); const world = new Ammo.btSoftRigidDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration); world.setGravity(new Ammo.btVector3(0, -9.8, 0)); // 创建地面 const groundShape = new Ammo.btBoxShape(new Ammo.btVector3(50, 1, 50)); const groundTransform = new Ammo.btTransform(); groundTransform.setIdentity(); groundTransform.setOrigin(new Ammo.btVector3(0, -1, 0)); const groundMass = 0; const groundLocalInertia = new Ammo.btVector3(0, 0, 0); const groundMotionState = new Ammo.btDefaultMotionState(groundTransform); const groundRigidBodyInfo = new Ammo.btRigidBodyConstructionInfo(groundMass, groundMotionState, groundShape, groundLocalInertia); const groundRigidBody = new Ammo.btRigidBody(groundRigidBodyInfo); world.addRigidBody(groundRigidBody); // 创建软体 const softBodyWorldInfo = new Ammo.btSoftBodyWorldInfo(); const softBodySolver = new Ammo.btDefaultSoftBodySolver(); softBodyWorldInfo.air_density = 1.2; softBodyWorldInfo.water_density = 0; softBodyWorldInfo.water_offset = 0; softBodyWorldInfo.water_normal = new Ammo.btVector3(0, 0, 0); softBodyWorldInfo.m_gravity.setValue(0, -9.8, 0); softBodyWorldInfo.m_sparsesdf.Initialize(); const clothCorner00 = new Ammo.btVector3(-5, 5, 0); const clothCorner01 = new Ammo.btVector3(5, 5, 0); const clothCorner10 = new Ammo.btVector3(-5, -5, 0); const clothCorner11 = new Ammo.btVector3(5, -5, 0); const clothSoftBody = Ammo.btSoftBodyHelpers.CreatePatch(softBodyWorldInfo, clothCorner00, clothCorner01, clothCorner10, clothCorner11, 10, 10, 0, true); clothSoftBody.getCollisionShape().setMargin(0.5); clothSoftBody.setTotalMass(0.1, false); world.addSoftBody(clothSoftBody); // 更新模拟 function update() { world.stepSimulation(1 / 60, 10); // 更新软体的位置 const softBodyNodes = clothSoftBody.get_m_nodes(); const nodeCount = softBodyNodes.size(); for (let i = 0; i < nodeCount; i++) { const node = softBodyNodes.at(i); const position = node.get_m_x(); console.log(`Node ${i}: x=${position.x()}, y=${position.y()}, z=${position.z()}`); } } // 每帧更新 function animate() { requestAnimationFrame(animate); update(); } animate(); ``` 这个例子创建了一个简单的软体模拟,包括一个地面和一个布料。你可以根据自己的需求进行修改和扩展。注意,这个例子是使用 ES6 模块语法,你需要使用支持模块的构建工具或浏览器环境来运行它。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值