Мир физики: btDiscreteDynamicsWorld

btDiscreteDynamicsWorld является центральным компонентом в архитектуре физического движка Ammo.js, предоставляя среду для симуляции динамики твёрдых тел. Он управляет объектами, столкновениями и интеграцией физических законов во времени. Этот класс наследует базовые возможности btDynamicsWorld, расширяя их функциями, оптимизированными для дискретного пошагового расчёта.

Ключевые функции btDiscreteDynamicsWorld:

  • Управление твёрдыми телами через массив объектов btRigidBody.
  • Обработка столкновений и разрешение контактных точек.
  • Интеграция движения тел с использованием дискретного временного шага.
  • Поддержка различных алгоритмов расчёта столкновений и сил.

Каждый шаг симуляции выполняется методом stepSimulation, который принимает время кадра, число подшагов и максимальный интервал подшагов. Это позволяет сохранять стабильность и предсказуемость физической модели при разных частотах кадров.

let deltaTime = 1 / 60; // время кадра
let maxSubSteps = 10;   // максимальное количество подшагов
dynamicsWorld.stepSimulation(deltaTime, maxSubSteps);

Создание динамического мира

Инициализация btDiscreteDynamicsWorld требует настройки нескольких компонентов:

  1. Конфигурация столкновений (btDefaultCollisionConfiguration) Определяет, какие алгоритмы расчёта столкновений будут использоваться. Для большинства задач достаточно стандартной конфигурации:

    let collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
  2. Менеджер обработки столкновений (btCollisionDispatcher) Отвечает за выбор конкретного алгоритма для каждой пары объектов:

    let dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
  3. Система broadphase (btDbvtBroadphase) Обеспечивает эффективное определение потенциальных столкновений на большом количестве объектов:

    let broadphase = new Ammo.btDbvtBroadphase();
  4. Решатель ограничений (btSequentialImpulseConstraintSolver) Вычисляет силы и импульсы для корректного взаимодействия тел:

    let solver = new Ammo.btSequentialImpulseConstraintSolver();
  5. Создание динамического мира

    let dynamicsWorld = new Ammo.btDiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration);
    dynamicsWorld.setGravity(new Ammo.btVector3(0, -9.81, 0));

Установка гравитации производится через объект btVector3, где каждый компонент задаёт силу по соответствующей оси.


Добавление твёрдых тел

Объекты физического мира представляются экземплярами btRigidBody. Их создание включает массу, форму и начальное состояние:

  1. Создание формы (btCollisionShape)

    let boxShape = new Ammo.btBoxShape(new Ammo.btVector3(1, 1, 1));

    Формы могут быть простыми (btBoxShape, btSphereShape) или сложными (btConvexHullShape, btBvhTriangleMeshShape).

  2. Определение массы и инерции

    let mass = 1;
    let localInertia = new Ammo.btVector3(0, 0, 0);
    boxShape.calculateLocalInertia(mass, localInertia);
  3. Создание тела (btRigidBody)

    let transform = new Ammo.btTransform();
    transform.setIdentity();
    transform.setOrigin(new Ammo.btVector3(0, 10, 0));
    let motionState = new Ammo.btDefaultMotionState(transform);
    let rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, boxShape, localInertia);
    let body = new Ammo.btRigidBody(rbInfo);
  4. Добавление тела в мир

    dynamicsWorld.addRigidBody(body);

Симуляция и обновление состояния

Метод stepSimulation не только вычисляет новые позиции и скорости объектов, но и позволяет управлять точностью через подшаги. Это критично для объектов с высокой скоростью или сложными столкновениями.

После шага симуляции можно получить текущее положение и ориентацию тела:

let motionState = body.getMotionState();
if (motionState) {
    let tmpTrans = new Ammo.btTransform();
    motionState.getWorldTransform(tmpTrans);
    let origin = tmpTrans.getOrigin();
    console.log(`x: ${origin.x()}, y: ${origin.y()}, z: ${origin.z()}`);
}

Обработка столкновений

btDiscreteDynamicsWorld автоматически вычисляет контактные точки, используя алгоритмы на основе btManifoldResult. Для доступа к столкновениям:

let numManifolds = dynamicsWorld.getDispatcher().getNumManifolds();
for (let i = 0; i < numManifolds; i++) {
    let contactManifold = dynamicsWorld.getDispatcher().getManifoldByIndexInternal(i);
    let numContacts = contactManifold.getNumContacts();
    for (let j = 0; j < numContacts; j++) {
        let pt = contactManifold.getContactPoint(j);
        if (pt.getDistance() < 0) {
            let posA = pt.getPositionWorldOnA();
            let posB = pt.getPositionWorldOnB();
            console.log(`Collision at A: ${posA.x()}, ${posA.y()}, ${posA.z()}`);
        }
    }
}

Это позволяет реализовать собственную логику реакции на столкновения, например звуковые эффекты или триггеры событий.


Управление ограничениями и силами

btDiscreteDynamicsWorld поддерживает ограничения (constraints), такие как шарниры, линкоры и пружины. Ограничения создаются через btTypedConstraint и добавляются в мир:

let hinge = new Ammo.btHingeConstraint(bodyA, bodyB, pivotInA, pivotInB, axisInA, axisInB, true);
dynamicsWorld.addConstraint(hinge, true);

Применение внешних сил выполняется через методы applyCentralForce, applyTorque и аналогичные:

body.applyCentralForce(new Ammo.btVector3(10, 0, 0));
body.applyTorque(new Ammo.btVector3(0, 5, 0));

Настройка производительности

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

  • Уменьшение количества подшагов при низкой скорости объектов.
  • Использование простых коллизий для больших количеств объектов.
  • Выключение расчёта спящих объектов с помощью setSleepingThresholds.

Пример:

body.setSleepingThresholds(0.5, 0.5);

Это позволяет исключить объекты, которые находятся в покое, из расчётов, что значительно ускоряет симуляцию.


Интеграция с визуализацией

Для синхронизации с рендерингом обычно берут позицию и ориентацию каждого тела после stepSimulation и передают их в графический движок (Three.js, Babylon.js или WebGL). Такой подход обеспечивает реалистичное движение объектов и точное отображение столкновений.


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