Процедурная генерация форм столкновений во время выполнения

Ammo.js — это порт библиотеки Bullet Physics на JavaScript с использованием WebAssembly. Она позволяет создавать физически реалистичные симуляции, включая динамику тел, столкновения и реакцию на силы. Основной единицей симуляции является rigid body — жёсткое тело, обладающее массой, инерцией и формой столкновения.

Для создания тела требуется:

  1. Форма столкновения (btCollisionShape) — описывает границы объекта для системы столкновений.
  2. Масса и инерция — масса задаёт динамику движения, инерция рассчитывается исходя из формы.
  3. Тело (btRigidBody) — соединяет форму и массу в полноценный физический объект.

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

let shape = new Ammo.btBoxShape(new Ammo.btVector3(1, 1, 1));
let mass = 1;
let localInertia = new Ammo.btVector3(0, 0, 0);
shape.calculateLocalInertia(mass, localInertia);

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

let motionState = new Ammo.btDefaultMotionState(transform);
let rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, shape, localInertia);
let body = new Ammo.btRigidBody(rbInfo);

physicsWorld.addRigidBody(body);

Динамическое создание форм столкновений

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

Типы форм столкновений

Ammo.js поддерживает несколько основных форм:

  • Примитивные формы: btBoxShape, btSphereShape, btCapsuleShape, btCylinderShape.
  • Составные (compound) формы: btCompoundShape позволяет объединять несколько простых форм в один объект.
  • Треугольные меши (btBvhTriangleMeshShape): используются для статических сложных объектов.
  • Convex Hull (btConvexHullShape): создаётся на основе набора вершин, полезна для динамических сложных тел.

Для процедурной генерации чаще всего применяются Convex Hull и Compound Shape, поскольку они позволяют строить форму на лету.

Создание Convex Hull на лету

btConvexHullShape принимает набор вершин и формирует минимальный выпуклый объём, который будет использоваться для столкновений:

let hullShape = new Ammo.btConvexHullShape();
vertices.forEach(v => {
    hullShape.addPoint(new Ammo.btVector3(v.x, v.y, v.z));
});
hullShape.optimizeConvexHull();
  • vertices — массив объектов с координатами {x, y, z}.
  • Метод optimizeConvexHull уменьшает количество вершин, ускоряя расчёты столкновений.

После создания формы можно использовать стандартную схему с массой и инерцией для добавления тела в мир.

Compound Shape для сложных объектов

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

let compound = new Ammo.btCompoundShape();

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

let boxTransform = new Ammo.btTransform();
boxTransform.setIdentity();
boxTransform.setOrigin(new Ammo.btVector3(0, 1, 0));

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

compound.addChildShape(boxTransform, boxShape);
compound.addChildShape(sphereTransform, sphereShape);

Compound Shape особенно полезна, когда нужно создавать динамические объекты из нескольких примитивов, например, процедурные роботы или строительные блоки.

Генерация сеток и треугольных мешей

Для статических объектов с высокой детализацией используется btBvhTriangleMeshShape. Процедурная генерация треугольной сетки может выполняться на основе:

  • Математических функций (плоские или волнистые поверхности).
  • Импортированных данных (OBJ, GLTF).
  • Генерации на лету (например, террейн, лабиринты).

Пример создания меша:

let triangleMesh = new Ammo.btTriangleMesh();
for (let i = 0; i < faces.length; i++) {
    let f = faces[i];
    triangleMesh.addTriangle(
        new Ammo.btVector3(f.a.x, f.a.y, f.a.z),
        new Ammo.btVector3(f.b.x, f.b.y, f.b.z),
        new Ammo.btVector3(f.c.x, f.c.y, f.c.z),
        true
    );
}

let meshShape = new Ammo.btBvhTriangleMeshShape(triangleMesh, true, true);
  • faces — массив треугольников с вершинами {a, b, c}.
  • Этот тип формы рекомендуется использовать только для статических объектов, так как динамическая симуляция на основе треугольных мешей очень затратна по вычислениям.

Управление трансформациями и динамическими изменениями

Для процедурной генерации важно уметь обновлять форму объекта после создания. Прямой замены формы в btRigidBody нет, поэтому используются следующие подходы:

  1. Удаление старого тела из мира и создание нового с обновлённой формой.
  2. Использование btCompoundShape с несколькими дочерними формами, где можно добавлять/удалять дочерние элементы динамически.
  3. Для временных эффектов или разрушений применяется подход с множеством маленьких тел, которые заменяют одно крупное.
physicsWorld.removeRigidBody(body);
Ammo.destroy(body);
body = new Ammo.btRigidBody(new Ammo.btRigidBodyConstructionInfo(mass, motionState, newShape, localInertia));
physicsWorld.addRigidBody(body);

Оптимизация процедурной генерации

  • Минимизация числа вершин: btConvexHullShape и btBvhTriangleMeshShape должны быть оптимизированы, иначе столкновения будут медленными.
  • Использование Compound Shape вместо многих отдельных тел: уменьшает количество расчётов столкновений.
  • Кэширование форм: если один и тот же тип формы создаётся многократно, лучше хранить экземпляр и клонировать его, чем пересоздавать заново.
  • Смешанные подходы: статические меши для окружения, Convex Hull для динамических тел.

Практические сценарии

  • Процедурный террейн: генерируются сегменты земли с треугольными мешами для статических тел.
  • Разрушаемые объекты: составные объекты разрезаются на Convex Hull при столкновении.
  • Процедурные враги и роботы: создаются из Compound Shape с динамически добавляемыми частями.
  • Физические эффекты генерации объектов: создание мячей, кубов и сложных тел во время игры на основе действий игрока или генератора случайных сцен.

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