В основе Ammo.js лежит движок физики твёрдых тел Bullet Physics — высокопроизводительная библиотека, написанная на C++ и широко применяемая в игровых движках и системах симуляции. Ammo.js представляет собой его порт в JavaScript, выполненный с использованием Emscripten, что позволяет запускать нативный C++-код в браузере через WebAssembly или asm.js.
Bullet Physics изначально проектировался как модульная система. Ammo.js наследует эту архитектуру практически без изменений, что означает сохранение тех же классов, структур данных и принципов работы:
Понимание внутренней структуры Bullet значительно упрощает работу с Ammo.js, поскольку API во многом повторяет оригинальную C++-модель.
Механизм столкновений делится на два уровня:
Широкофазная проверка (Broadphase) Определяет потенциально пересекающиеся объекты, используя упрощённые bounding volumes. В Ammo.js доступны реализации:
btDbvtBroadphasebtAxisSweep3Broadphase уменьшает количество проверок, передавая только кандидатов в следующую фазу.
Узкофазная проверка (Narrowphase) Выполняет точные вычисления пересечения геометрий. Используются алгоритмы:
В Ammo.js это реализовано через
btCollisionDispatcher.
Физический мир в Bullet строится из нескольких обязательных компонентов:
const collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
const dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
const broadphase = new Ammo.btDbvtBroadphase();
const solver = new Ammo.btSequentialImpulseConstraintSolver();
const dynamicsWorld = new Ammo.btDiscreteDynamicsWorld(
dispatcher,
broadphase,
solver,
collisionConfiguration
);
Каждый компонент отвечает за конкретную часть пайплайна:
Bullet Physics реализует динамику через класс
btRigidBody. Ammo.js сохраняет ту же структуру:
Пример создания динамического тела:
const shape = new Ammo.btBoxShape(new Ammo.btVector3(1, 1, 1));
const transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(0, 10, 0));
const motionState = new Ammo.btDefaultMotionState(transform);
const mass = 1;
const localInertia = new Ammo.btVector3(0, 0, 0);
shape.calculateLocalInertia(mass, localInertia);
const rbInfo = new Ammo.btRigidBodyConstructionInfo(
mass,
motionState,
shape,
localInertia
);
const body = new Ammo.btRigidBody(rbInfo);
dynamicsWorld.addRigidBody(body);
В Bullet используется импульсный метод решения ограничений. Ammo.js
по умолчанию применяет
btSequentialImpulseConstraintSolver.
Алгоритм работы:
Параметры решателя влияют на стабильность:
dynamicsWorld.getSolverInfo().set_m_numIterations(10);
Увеличение числа итераций повышает точность, но увеличивает нагрузку.
Bullet Physics предлагает широкий набор форм:
btBoxShapebtSphereShapebtCapsuleShapebtCylinderShapebtConvexHullShapebtBvhTriangleMeshShapeConvex vs Concave
Для оптимизации часто используется составная форма:
const compound = new Ammo.btCompoundShape();
compound.addChildShape(localTransform, childShape);
Основной цикл:
dynamicsWorld.stepSimulation(deltaTime, maxSubSteps);
Параметры:
deltaTime — прошедшее время;maxSubSteps — максимальное количество внутренних
шагов.Bullet использует фиксированный внутренний timestep для стабильности.
Bullet поддерживает мягкие тела через
btSoftRigidDynamicsWorld. Ammo.js сохраняет эту
функциональность при сборке с соответствующими флагами.
Soft body включает:
Для этого требуется отдельная конфигурация:
const softBodySolver = new Ammo.btDefaultSoftBodySolver();
Bullet реализует физические связи:
btPoint2PointConstraintbtHingeConstraintbtSliderConstraintbtConeTwistConstraintbtGeneric6DofConstraintПример шарнирного соединения:
const hinge = new Ammo.btHingeConstraint(
bodyA,
bodyB,
pivotA,
pivotB,
axisA,
axisB
);
dynamicsWorld.addConstraint(hinge, true);
Constraints создают кинематические связи, которые затем обрабатываются решателем.
Поскольку Ammo.js — порт C++ в WebAssembly, возникают особенности:
Объекты Ammo не управляются сборщиком мусора JavaScript. Необходимо вручную освобождать память:
Ammo.destroy(object);
Иначе возможны утечки памяти.
Современные сборки Ammo.js используют WebAssembly, что обеспечивает почти нативную производительность по сравнению с C++-версией Bullet Physics.
Сложность широкофазной проверки растёт с числом объектов. Оптимальные практики:
Bullet работает по следующей схеме:
Ammo.js полностью повторяет этот цикл.
MotionState используется для синхронизации физики и графики:
const transform = new Ammo.btTransform();
body.getMotionState().getWorldTransform(transform);
const origin = transform.getOrigin();
const rotation = transform.getRotation();
Полученные данные передаются в движок визуализации (например, Three.js), обеспечивая корректное отображение физического состояния.
При этом API максимально близок к оригиналу, что позволяет использовать документацию C++-версии Bullet Physics.
Bullet Physics используется во множестве профессиональных инструментов и игровых систем благодаря:
Ammo.js переносит эти преимущества в среду браузера, сохраняя архитектурные принципы и алгоритмическую основу оригинального движка.