基础环境搭建:
当Ammo.js库初始化完成后进入我们代码:
Ammo().then(() => {
initGraphicUniverse()
initPhysicsUniverse()
render()
})
bullet基础环境配置,初始化物理世界:
btDefaultCollisionConfiguration配置通过Bullet(Ammo.js)检测碰撞。
btCollisionDispatcher负责管理凸和凹碰撞的算法。
btsequentialimpulse seconstraintsolver允许在我们的宇宙中解决物理规则的约束(重力,力…)
btdiscretedynamicsworld对应我们的动态世界; 我们的可变物理宇宙就是这种类型。
function initPhysicsUniverse() {
const collisionConfiguration = new Ammo.btDefaultCollisionConfiguration()
const dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration)
const overlappingPairCache = new Ammo.btDbvtBroadphase()
const solver = new Ammo.btSequentialImpulseConstraintSolver()
physicsUniverse = new Ammo.btDiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration)
physicsUniverse.setGravity(new Ammo.btVector3(0, -75, 0))
}
three基础环境配置在initGraphics()中。
创建场景中具有物理特性的立方体:
function createCube(scale, position, mass, rot_quaternion) {
let quaternion
console.log(position)
if (rot_quaternion == null) {
quaternion = { x: 0, y: 0, z: 0, w: 0 }
}
else {
quaternion = rot_quaternion
}
}
图形端,正常创建网格模型并添加到场景中:
//graphic world
const cube = new THREE.Mesh(
new THREE.BoxGeometry(scale, scale, scale),
new THREE.MeshPhongMaterial({ color: Math.random() * 0xffffff })
)
cube.position.set(position.x, position.y, position.z)
scene.add(cube)
物理端,创建刚体:
刚体和碰撞的几何结构概念
我们称物理世界中受动态力影响的所有实体为刚体(具有质量、速度和能够进行碰撞等)。
因此,为了正确地模拟动力有必要在我们的物理世界中创建刚体。为此,我们希望包含在物理模拟中的每个3D对象(图像世界中的)都将拥有自己的刚体(在物理世界中)。
然而,模拟的每个3D对象并不具有相同的形式,因此并不会在相同的物理作用场景中做出相同的反应。例如,将立方形刚体堆起来比堆球形刚体更容易!
这就是为什么刚体并不都具有相同的几何形式。 这就是我们所说的碰撞的几何结构。
让我们用一个简单的例子说明:我们希望创建一个对物理有反应的3D立方体。在图形端我们将创建一个由BoxGeometry几何体对象和材质对象组成的网格模型。接下来,在物理端创建一个具有立方体碰撞几何结构、类型为btBoxShape刚体。
创建默认运动状态,定义物理世界对象初始位置和旋转角度:
//physics world
let transform = new Ammo.btTransform()
transform.setIdentity()
transform.setOrigin(new Ammo.btVector3(position.x, position.y, position.z))
transform.setRotation(new Ammo.btQuaternion(quaternion.x, quaternion.y, quaternion.z, quaternion.w))
let defaultMotionState = new Ammo.btDefaultMotionState(transform)
定义对象的碰撞几何体结构:
let structColShape = new Ammo.btBoxShape(new Ammo.btVector3(scale * 0.5, scale * 0.5, scale * 0.5))
structColShape.setMargin(0.05)
处理初始惯性的计算(如果定义了一个初始旋转,这很重要):
let localInertia = new Ammo.btVector3(0, 0, 0)
structColShape.calculateLocalInertia(mass, localInertia)
使用我们刚刚初始化的元素创建刚体:
let RBody_Info = new Ammo.btRigidBodyConstructionInfo(mass, defaultMotionState, structColShape, localInertia)
let Rbody = new Ammo.btRigidBody(RBody_Info)
添加刚体到物理世界中:
physicsUniverse.addRigidBody(Rbody)
将这个新刚体定义为立方体的userData.physicsBody属性值(这就是物理和图形世界之间的联系):
cube.userData.physicsBody = Rbody
rigidBody_list.push(cube)
调用createCube():
function createObjects() {
createCube(40, new THREE.Vector3(15, -30, 15), 0, null)
createCube(4, new THREE.Vector3(0, 10, 0), 1, null)
createCube(2, new THREE.Vector3(10, 30, 0), 1, null);
createCube(4, new THREE.Vector3(10, 20, 10), 1, null);
createCube(6, new THREE.Vector3(5, 40, 20), 1, null);
createCube(8, new THREE.Vector3(25, 100, 5), 1, null);
createCube(8, new THREE.Vector3(20, 60, 25), 1, null);
createCube(4, new THREE.Vector3(20, 100, 25), 1, null);
createCube(2, new THREE.Vector3(20, 200, 25), 1, null);
}
更新针对时间的物理模拟:
使用stepSimulation()将动态模拟作为运行时间的函数来更新:
function updatePhysicsUniverse(delta) {
physicsUniverse.stepSimulation(delta, 10)
}
创建针对rigidBody_List数组的循环,分别获取当前循环的图形对象和物理对象:
for (let i = 0; i < rigidBody_list.length; i++) {
let graphicObj = rigidBody_list[i]
let physicsObj = rigidBody_list[i].userData.physicsBody
}
从物理世界中提取位置和旋转属性,通过动态模拟更新,并将它们注入到图形世界中。这样通过物理世界的模拟带来的属性变化将在图形世界中可见:
创建全局变量tmpTransformation暂时存储应用在每次循环中的变化:
let tmpTranformation
...
Ammo().then(() => {
tmpTranformation = new Ammo.btTransform()
...
})
提取每个物理对象的位置和旋转属性并注入到对应的图形对象中:
let motionState = physicsObj.getMotionState()
if (motionState) {
motionState.getWorldTransform(tmpTranformation)
let newPos = tmpTranformation.getOrigin()
let newQua = tmpTranformation.getRotation()
graphicObj.position.set(newPos.x(), newPos.y(), newPos.z())
graphicObj.rotation.set(newQua.x(), newQua.y(), newQua.z(), newQua.w())
}
从现在起,在每次调用updatePhysicsUniverse时,物理和图形世界将同步。
渲染、调用:
function render()
{
let deltaTime = clock.getDelta();
updatePhysicsUniverse( deltaTime );
renderer.render( scene, camera );
requestAnimationFrame( render );
}
Ammo().then(function () {
tmpTransformation = new Ammo.btTransform()
initGraphicUniverse()
initPhysicsUniverse()
createObjects()
render()
})
实现立方体自由落体: