btCompoundShape: составные формы из нескольких примитивов

Библиотека Ammo.js предоставляет мощный инструментарий для работы с физикой в 3D-пространстве, и одним из ключевых элементов для сложных коллизий является класс btCompoundShape. Этот класс позволяет создавать составные коллизионные формы, объединяя несколько простых примитивов, таких как коробки, сферы или цилиндры, в единую физическую сущность.

Основные свойства и принципы работы

btCompoundShape наследует функциональность базового интерфейса коллизионных объектов. Он не хранит напрямую геометрию каждого примитива, а поддерживает массив вложенных форм с локальными трансформациями. Каждая вложенная форма (child shape) имеет собственное смещение и ориентацию относительно центра масс составной формы. Это позволяет моделировать сложные объекты, например машины, роботов или мебели, с высокой точностью коллизий.

Ключевые моменты:

  • Локальные трансформации: каждая добавляемая форма имеет btTransform, задающий её позицию и вращение внутри составной формы.
  • Центр масс: вычисляется автоматически на основе всех вложенных форм, но может быть скорректирован вручную.
  • Динамическое добавление/удаление: формы можно добавлять или удалять в процессе выполнения, но это требует обновления матриц инерции для корректного расчёта физики.

Создание и добавление примитивов

Создание составной формы начинается с инициализации объекта btCompoundShape:

let compoundShape = new Ammo.btCompoundShape();

Для добавления примитива создаётся соответствующая форма, например, btBoxShape:

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

Далее формируется локальная трансформация:

let transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(0, 2, 0));

И добавляется к составной форме:

compoundShape.addChildShape(transform, boxShape);

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

compoundShape.calculatePrincipalAxisTransform(new Ammo.btTransform(), new Ammo.btVector3());

Управление вложенными формами

btCompoundShape предоставляет несколько методов для работы с дочерними объектами:

  • getNumChildShapes() — возвращает количество примитивов.
  • getChildShape(index) — возвращает форму по индексу.
  • getChildTransform(index) — возвращает локальную трансформацию конкретного примитива.
  • updateChildTransform(index, newTransform, shouldRecalculateLocalAabb) — позволяет изменить трансформацию примитива после добавления. Параметр shouldRecalculateLocalAabb отвечает за пересчёт локального AABB (Axis-Aligned Bounding Box) для корректного расчёта столкновений.

Пример изменения позиции примитива:

let newTransform = new Ammo.btTransform();
newTransform.setIdentity();
newTransform.setOrigin(new Ammo.btVector3(0, 3, 0));
compoundShape.updateChildTransform(0, newTransform, true);

Использование btCompoundShape с RigidBody

Составные формы чаще всего применяются вместе с btRigidBody для динамических объектов:

let mass = 10;
let localInertia = new Ammo.btVector3(0, 0, 0);
compoundShape.calculateLocalInertia(mass, localInertia);

let motionState = new Ammo.btDefaultMotionState();
let rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, compoundShape, localInertia);
let rigidBody = new Ammo.btRigidBody(rbInfo);

Важно учитывать, что расчёт инерции (calculateLocalInertia) обязателен для динамических тел, иначе объект будет вести себя как статический. Для статических объектов с mass = 0 вызов не требуется.

Оптимизация и практические советы

  • Минимизация количества примитивов: каждый добавленный объект увеличивает вычислительную нагрузку, поэтому следует объединять геометрию там, где это возможно.
  • Использование простых форм: сложные меши стоит разбивать на комбинацию простых примитивов вместо использования сложных треугольных сеток.
  • Обновление трансформаций: при динамическом изменении формы необходимо учитывать пересчёт локального AABB, иначе столкновения могут работать некорректно.
  • Центр масс: для симметричных объектов btCompoundShape автоматически определяет центр масс, но для асимметричных форм можно задать смещение вручную через calculatePrincipalAxisTransform.

Примеры практического применения

  • Транспортные средства: колёса, кузов и подвеска моделируются отдельными формами внутри одной составной формы.
  • Игровые персонажи и роботы: сложные суставные конструкции могут быть разбиты на примитивы, соединённые через btCompoundShape, обеспечивая точные столкновения.
  • Мебель и строительные объекты: столы, стулья, лестницы создаются как набор коробок и цилиндров, что значительно упрощает симуляцию.

Использование btCompoundShape позволяет балансировать между точностью коллизий и производительностью, обеспечивая гибкость при моделировании объектов любой сложности в Ammo.js.