btDiscreteDynamicsWorld является центральным компонентом
в архитектуре физического движка Ammo.js, предоставляя
среду для симуляции динамики твёрдых тел. Он управляет объектами,
столкновениями и интеграцией физических законов во времени. Этот класс
наследует базовые возможности btDynamicsWorld, расширяя
их функциями, оптимизированными для дискретного пошагового расчёта.
Ключевые функции
btDiscreteDynamicsWorld:
btRigidBody.Каждый шаг симуляции выполняется методом stepSimulation,
который принимает время кадра, число подшагов и
максимальный интервал подшагов. Это позволяет сохранять стабильность и
предсказуемость физической модели при разных частотах кадров.
let deltaTime = 1 / 60; // время кадра
let maxSubSteps = 10; // максимальное количество подшагов
dynamicsWorld.stepSimulation(deltaTime, maxSubSteps);
Инициализация btDiscreteDynamicsWorld требует настройки
нескольких компонентов:
Конфигурация столкновений
(btDefaultCollisionConfiguration) Определяет,
какие алгоритмы расчёта столкновений будут использоваться. Для
большинства задач достаточно стандартной конфигурации:
let collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();Менеджер обработки столкновений
(btCollisionDispatcher) Отвечает за выбор
конкретного алгоритма для каждой пары объектов:
let dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);Система broadphase
(btDbvtBroadphase) Обеспечивает эффективное
определение потенциальных столкновений на большом количестве
объектов:
let broadphase = new Ammo.btDbvtBroadphase();Решатель ограничений
(btSequentialImpulseConstraintSolver) Вычисляет
силы и импульсы для корректного взаимодействия тел:
let solver = new Ammo.btSequentialImpulseConstraintSolver();Создание динамического мира
let dynamicsWorld = new Ammo.btDiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration);
dynamicsWorld.setGravity(new Ammo.btVector3(0, -9.81, 0));Установка гравитации производится через объект
btVector3, где каждый компонент задаёт силу по
соответствующей оси.
Объекты физического мира представляются экземплярами
btRigidBody. Их создание включает массу, форму и
начальное состояние:
Создание формы
(btCollisionShape)
let boxShape = new Ammo.btBoxShape(new Ammo.btVector3(1, 1, 1));
Формы могут быть простыми (btBoxShape,
btSphereShape) или сложными
(btConvexHullShape,
btBvhTriangleMeshShape).
Определение массы и инерции
let mass = 1;
let localInertia = new Ammo.btVector3(0, 0, 0);
boxShape.calculateLocalInertia(mass, localInertia);Создание тела (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);Добавление тела в мир
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
предоставляет мощный инструмент для создания реалистичных физических
сцен, обеспечивая точное управление динамикой, столкновениями и
ограничениями, а также гибкую настройку под конкретные задачи
симуляции.