Составные тела из множества форм: плюсы и особенности

В физическом движке Oimo.js каждое физическое тело (RigidBody) может содержать одну или несколько геометрических форм (Shape). Тело с несколькими формами называется составным (compound body). Такая конструкция позволяет описывать сложную геометрию без необходимости создавать множество отдельных тел и соединять их ограничениями.

Составное тело — это один динамический объект с:

  • единым центром масс,
  • единой скоростью и угловой скоростью,
  • общей инерцией,
  • несколькими формами столкновения.

Формы могут иметь собственные локальные смещения и ориентацию относительно центра тела.


Архитектура составного тела

Внутри движка структура выглядит следующим образом:

  • World — физический мир.
  • RigidBody — физическое тело.
  • Shape — геометрическая форма (Box, Sphere, Cylinder и др.).
  • ShapeConfig — конфигурация формы.

Каждая форма содержит:

  • локальное положение,
  • локальную ориентацию,
  • параметры материала (трение, упругость),
  • ссылку на родительское тело.

При создании нескольких форм для одного тела движок автоматически:

  1. Объединяет их в единую структуру коллизий.
  2. Пересчитывает центр масс.
  3. Вычисляет итоговый тензор инерции.

Зачем нужны составные тела

1. Описание сложной геометрии

Oimo.js использует примитивы (box, sphere, cylinder) для коллизий. Неправильные многогранники или модели произвольной формы напрямую не поддерживаются. Составное тело решает проблему: сложная фигура аппроксимируется набором простых форм.

Пример: автомобиль можно представить как:

  • основной корпус (Box),
  • 4 колеса (Cylinder или Sphere),
  • дополнительные элементы.

2. Повышение стабильности симуляции

Соединение нескольких тел через Joint увеличивает нагрузку на солвер ограничений. Составное тело:

  • не требует дополнительных ограничений,
  • не создаёт избыточных степеней свободы,
  • устойчивее при расчётах.

3. Производительность

Один RigidBody с несколькими формами:

  • дешевле, чем несколько тел + соединения,
  • требует меньше итераций солвера,
  • упрощает управление.

Создание составного тела

Пример создания тела с двумя формами:

var world = new OIMO.World();

var bodyConfig = new OIMO.RigidBodyConfig();
bodyConfig.type = OIMO.RigidBodyType.DYNAMIC;
bodyConfig.position.init(0, 5, 0);

var body = new OIMO.RigidBody(bodyConfig);
world.addRigidBody(body);

// Первая форма — основной блок
var shapeConfig1 = new OIMO.ShapeConfig();
shapeConfig1.geometry = new OIMO.BoxGeometry(new OIMO.Vec3(1, 1, 1));

var shape1 = new OIMO.Shape(shapeConfig1);
body.addShape(shape1);

// Вторая форма — смещённый блок
var shapeConfig2 = new OIMO.ShapeConfig();
shapeConfig2.geometry = new OIMO.BoxGeometry(new OIMO.Vec3(0.5, 0.5, 0.5));
shapeConfig2.position.init(1.5, 0, 0);

var shape2 = new OIMO.Shape(shapeConfig2);
body.addShape(shape2);

// Обновление масс-инерционных характеристик
body.setupMass(OIMO.RigidBodyType.DYNAMIC);

Ключевой момент: после добавления всех форм необходимо вызвать setupMass, иначе центр масс и инерция не будут корректно пересчитаны.


Локальные координаты форм

Каждая форма располагается в локальной системе координат тела:

  • shapeConfig.position — смещение относительно центра тела.
  • shapeConfig.rotation — локальная ориентация.

Это позволяет создавать асимметричные конструкции.

Влияние на центр масс

Если формы расположены несимметрично, центр масс будет смещён. Движок автоматически вычисляет новый центр и пересчитывает:

  • массу,
  • тензор инерции,
  • главный момент инерции.

Это влияет на поведение тела при вращении. Например:

  • смещённая масса вызывает нестабильное вращение,
  • асимметрия приводит к естественному “переворачиванию”.

Расчёт массы и инерции

При использовании setupMass Oimo.js:

  1. Суммирует массу всех форм.
  2. Применяет теорему Штейнера (параллельных осей).
  3. Формирует итоговый тензор инерции тела.

Масса формы зависит от:

  • плотности (density),
  • объёма геометрии.

Если плотность не указана, используется значение по умолчанию.

Пример задания плотности:

shapeConfig1.density = 2;
shapeConfig2.density = 1;

Это позволяет контролировать распределение веса внутри составного тела.


Столкновения составных тел

Каждая форма участвует в широкой (broadphase) и узкой (narrowphase) фазе коллизий.

Особенности:

  • Коллизия рассчитывается для каждой формы отдельно.
  • Контактные точки объединяются в один набор для тела.
  • Импульсы применяются к общему RigidBody.

Если одна из форм касается поверхности, сила реакции воздействует на всё тело с учётом плеча приложения силы, что создаёт корректный вращательный момент.


Ограничения и подводные камни

1. Отсутствие динамического добавления форм

Добавление формы после начала симуляции требует пересчёта массы. Если это сделать неправильно, возможны:

  • скачки положения,
  • некорректная инерция,
  • нестабильность.

Рекомендуется:

  • останавливать тело,
  • удалять его из мира,
  • добавлять формы,
  • пересчитывать массу,
  • заново добавлять в мир.

2. Большое количество форм

Слишком сложная аппроксимация приводит к:

  • росту числа проверок коллизий,
  • увеличению нагрузки на narrowphase,
  • падению FPS.

Оптимально использовать 3–10 форм для одного объекта в реальных задачах.

3. Центр масс не совпадает с визуальной моделью

Графическая модель (например, в Three.js) может иметь центр в другом месте. Если не учитывать это, визуальная сцена будет выглядеть некорректно.

Решение:

  • синхронизировать смещение визуального объекта с центром масс физического тела,
  • либо заранее корректировать локальные координаты форм.

Составные тела и связки (Joint)

Если требуется жёсткое соединение нескольких элементов без относительного движения, есть два варианта:

  1. Несколько тел + FixedJoint
  2. Одно составное тело

Составное тело предпочтительнее, если:

  • нет необходимости в разрушении конструкции,
  • элементы не должны отделяться,
  • важна производительность.

Использование Joint оправдано при:

  • шарнирных соединениях,
  • пружинных механизмах,
  • разрушаемых структурах.

Типовые сценарии применения

Механические объекты

  • молотки,
  • штанги,
  • инструменты со смещённым центром тяжести.

Архитектурные элементы

  • сложные колонны,
  • Т-образные балки,
  • ступенчатые конструкции.

Игровые объекты

  • персонажи из примитивов,
  • препятствия нестандартной формы,
  • транспорт.

Оптимизация составных тел

Минимизация числа форм

Лучше использовать:

  • один большой box вместо нескольких мелких,
  • сферу вместо набора цилиндров, если это допустимо.

Упрощение геометрии

Физическая модель не обязана точно повторять визуальную. Допускается грубая аппроксимация, если это не влияет на геймплей.

Использование статических тел

Если объект не движется, тип STATIC:

  • отключает расчёт динамики,
  • снижает нагрузку,
  • ускоряет симуляцию.

Взаимодействие с вращением

Асимметричное распределение форм напрямую влияет на:

  • устойчивость,
  • прецессию,
  • угловое ускорение.

При резком приложении импульса:

  • момент инерции определяет скорость вращения,
  • смещение центра масс влияет на траекторию.

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


Сравнение с другими подходами

В движках вроде Cannon.js и Ammo.js используется аналогичный принцип compound shapes. Однако в Oimo.js структура более минималистична и ориентирована на простоту API.

Преимущества реализации в Oimo.js:

  • компактность,
  • понятная конфигурация,
  • низкий порог входа,
  • хорошая производительность в браузере.

Составные тела являются ключевым инструментом моделирования сложных объектов в Oimo.js. Правильное использование множества форм внутри одного RigidBody обеспечивает баланс между реализмом, производительностью и архитектурной чистотой физической системы.