Ammo.js — это порт популярного физического движка Bullet Physics на JavaScript с использованием WebAssembly. В сочетании с Babylon.js он позволяет создавать реалистичную физику в 3D-сценах: столкновения, гравитацию, силы инерции и ограничения между объектами. Для интеграции необходимо понимать, как синхронизировать объекты сцены с физическим миром Ammo.js.
Физический мир создается через объект
Ammo.btDiscreteDynamicsWorld. Основные компоненты:
Пример:
const collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
const dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
const broadphase = new Ammo.btDbvtBroadphase();
const solver = new Ammo.btSequentialImpulseConstraintSolver();
const physicsWorld = new Ammo.btDiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration);
physicsWorld.setGravity(new Ammo.btVector3(0, -9.81, 0));
Физические объекты делятся на статические, динамические и кинематические. Статические не подвержены физике (например, пол), динамические реагируют на силы, крутящий момент и столкновения.
Форма столкновений
(btCollisionShape):
Массивные свойства:
0.btDefaultMotionState) синхронизирует положение с
визуальной моделью Babylon.js.Пример динамического куба:
const boxSize = new Ammo.btVector3(1, 1, 1);
const boxShape = new Ammo.btBoxShape(boxSize);
const mass = 1;
const localInertia = new Ammo.btVector3(0, 0, 0);
boxShape.calculateLocalInertia(mass, localInertia);
const transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(0, 5, 0));
const motionState = new Ammo.btDefaultMotionState(transform);
const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, boxShape, localInertia);
const body = new Ammo.btRigidBody(rbInfo);
physicsWorld.addRigidBody(body);
Чтобы визуальные объекты соответствовали физическим, необходимо обновлять их трансформации на каждом кадре. Основной цикл:
scene.registerBeforeRender(() => {
const deltaTime = engine.getDeltaTime() / 1000;
physicsWorld.stepSimulation(deltaTime, 10);
// Применение к объектам Babylon.js
body.getMotionState().getWorldTransform(transform);
const origin = transform.getOrigin();
const rotation = transform.getRotation();
mesh.position.set(origin.x(), origin.y(), origin.z());
mesh.rotationQuaternion = new BABYLON.Quaternion(rotation.x(), rotation.y(), rotation.z(), rotation.w());
});
Ключевой момент — использовать
rotationQuaternion вместо обычного вращения Euler, так как
Ammo.js работает с кватернионами.
Ammo.js позволяет прикладывать силы и импульсы к телам:
// Сила, прикладываемая в течение кадра
body.applyCentralForce(new Ammo.btVector3(10, 0, 0));
// Мгновенный импульс
body.applyCentralImpulse(new Ammo.btVector3(0, 5, 0));
// Вращающий момент
body.applyTorque(new Ammo.btVector3(0, 10, 0));
Различие между силой и импульсом критично: сила накапливается во времени, а импульс действует мгновенно.
Ammo.js поддерживает разнообразные constraints для соединения объектов:
Пример шарнирного соединения:
const pivotA = new Ammo.btVector3(0, -1, 0);
const pivotB = new Ammo.btVector3(0, 1, 0);
const hinge = new Ammo.btHingeConstraint(bodyA, bodyB, pivotA, pivotB, true);
physicsWorld.addConstraint(hinge, true);
stepSimulation) для слабых устройств.Для сложных 3D-мешей Babylon.js применяется
btConvexHullShape:
const vertices = mesh.getVerticesData(BABYLON.VertexBuffer.PositionKind);
const shape = new Ammo.btConvexHullShape();
for (let i = 0; i < vertices.length; i += 3) {
shape.addPoint(new Ammo.btVector3(vertices[i], vertices[i + 1], vertices[i + 2]));
}
Важно предварительно упрощать геометрию для физических расчетов, чтобы избежать падения FPS.
Babylon.js предоставляет обертку через AmmoJSPlugin:
const gravityVector = new BABYLON.Vector3(0, -9.81, 0);
const physicsPlugin = new BABYLON.AmmoJSPlugin();
scene.enablePhysics(gravityVector, physicsPlugin);
Это упрощает создание тел:
const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", { diameter: 1 });
sphere.physicsImpostor = new BABYLON.PhysicsImpostor(
sphere,
BABYLON.PhysicsImpostor.SphereImpostor,
{ mass: 1, restitution: 0.9 },
scene
);
Использование PhysicsImpostor автоматически
синхронизирует позицию и вращение объекта с физическим телом.
Ammo.js позволяет регистрировать коллизии через пользовательский callback или проверку контактных точек:
const numManifolds = dispatcher.getNumManifolds();
for (let i = 0; i < numManifolds; i++) {
const manifold = dispatcher.getManifoldByIndexInternal(i);
const bodyA = manifold.getBody0();
const bodyB = manifold.getBody1();
const numContacts = manifold.getNumContacts();
if (numContacts > 0) {
// Обработка столкновения
}
}
Это важно для игровых событий: удар, падение, триггеры.
interpolation) между кадрами улучшает визуальную плавность
при низкой частоте шага физики.Ammo.js в сочетании с Babylon.js позволяет строить сложные физические симуляции, от реалистичных разрушений до мехатронных систем, с точной синхронизацией между визуальной сценой и физикой. Правильная организация тела, ограничений и синхронизации обеспечивает высокую производительность и достоверность движений.