Three Ammo实现物理作用实例

在这里插入图片描述

基础环境搭建:

当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()
})

实现立方体自由落体:
在这里插入图片描述

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值