Интеграция с Babylon.js

Ammo.js — это порт популярного физического движка Bullet Physics на JavaScript с использованием WebAssembly. В сочетании с Babylon.js он позволяет создавать реалистичную физику в 3D-сценах: столкновения, гравитацию, силы инерции и ограничения между объектами. Для интеграции необходимо понимать, как синхронизировать объекты сцены с физическим миром Ammo.js.


Инициализация физического мира

Физический мир создается через объект Ammo.btDiscreteDynamicsWorld. Основные компоненты:

  • btDefaultCollisionConfiguration — хранит конфигурацию столкновений.
  • btCollisionDispatcher — обрабатывает коллизии между объектами.
  • btDbvtBroadphase — оптимизированная структура для определения потенциальных столкновений.
  • btSequentialImpulseConstraintSolver — решатель физических ограничений.

Пример:

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));

Создание физических тел

Физические объекты делятся на статические, динамические и кинематические. Статические не подвержены физике (например, пол), динамические реагируют на силы, крутящий момент и столкновения.

  1. Форма столкновений (btCollisionShape):

    • btBoxShape — коробка.
    • btSphereShape — сфера.
    • btCapsuleShape — капсула.
    • btConvexHullShape — сложная форма на основе вершин.
  2. Массивные свойства:

    • Масса определяет динамику объекта. Статические объекты имеют массу 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);

Синхронизация с Babylon.js

Чтобы визуальные объекты соответствовали физическим, необходимо обновлять их трансформации на каждом кадре. Основной цикл:

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 для соединения объектов:

  • Point-to-Point (btPoint2PointConstraint) — фиксированная точка соединения.
  • Hinge (btHingeConstraint) — шарнир с одной осью вращения.
  • Slider (btSliderConstraint) — движение вдоль оси.
  • ConeTwistConstraint — ограниченное вращение в конусе.

Пример шарнирного соединения:

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);

Оптимизация производительности

  1. Использование Broadphase снижает количество проверок коллизий.
  2. Статические объекты — всегда с массой 0.
  3. Снижение частоты шагов физики (stepSimulation) для слабых устройств.
  4. Рециклинг Ammo.btVector3 вместо создания новых объектов на каждом кадре.

Работа с нестандартными формами

Для сложных 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 Physics Plugin

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) между кадрами улучшает визуальную плавность при низкой частоте шага физики.
  • Настройка restitution и friction на физических объектах позволяет добиться реалистичных отскоков и трения.

Ammo.js в сочетании с Babylon.js позволяет строить сложные физические симуляции, от реалистичных разрушений до мехатронных систем, с точной синхронизацией между визуальной сценой и физикой. Правильная организация тела, ограничений и синхронизации обеспечивает высокую производительность и достоверность движений.