Compound shape в Ammo.js — это
физическая конструкция, позволяющая объединять несколько простых
коллайдеров (box, sphere, cylinder, convex hull) в единое тело. Такая
структура идеально подходит для создания разрушаемых объектов, где
сложная геометрия не может быть адекватно смоделирована одной
примитивной формой. Основная идея заключается в том, что каждый дочерний
коллайдер добавляется с собственным смещением и ориентацией относительно
центра массы общего объекта.
let compoundShape = new Ammo.btCompoundShape();
let boxShape = new Ammo.btBoxShape(new Ammo.btVector3(1, 1, 1));
let sphereShape = new Ammo.btSphereShape(0.5);
let transformBox = new Ammo.btTransform();
transformBox.setIdentity();
transformBox.setOrigin(new Ammo.btVector3(0, 0, 0));
let transformSphere = new Ammo.btTransform();
transformSphere.setIdentity();
transformSphere.setOrigin(new Ammo.btVector3(1.5, 0, 0));
compoundShape.addChildShape(transformBox, boxShape);
compoundShape.addChildShape(transformSphere, sphereShape);
Ключевой момент: compoundShape сам по себе не имеет
геометрической массы — масса задается телу (rigid body), к которому он
привязан.
Разрушаемые объекты часто моделируются с использованием
compound shape, где каждый фрагмент становится дочерним
элементом. При ударе или столкновении можно разделять
compound shape на отдельные тела, чтобы реализовать эффект
разрушения.
Пошаговая схема:
btCompoundShape. Объединение
всех фрагментов с нужными смещениями.Пример базового создания разрушаемого блока:
function createDestructibleBlock(position) {
let compound = new Ammo.btCompoundShape();
let cube1 = new Ammo.btBoxShape(new Ammo.btVector3(0.5, 0.5, 0.5));
let cube2 = new Ammo.btBoxShape(new Ammo.btVector3(0.5, 0.5, 0.5));
let t1 = new Ammo.btTransform();
t1.setIdentity();
t1.setOrigin(new Ammo.btVector3(-0.5, 0, 0));
let t2 = new Ammo.btTransform();
t2.setIdentity();
t2.setOrigin(new Ammo.btVector3(0.5, 0, 0));
compound.addChildShape(t1, cube1);
compound.addChildShape(t2, cube2);
let mass = 2;
let localInertia = new Ammo.btVector3(0, 0, 0);
compound.calculateLocalInertia(mass, localInertia);
let transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(position.x, position.y, position.z));
let motionState = new Ammo.btDefaultMotionState(transform);
let rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, compound, localInertia);
let body = new Ammo.btRigidBody(rbInfo);
physicsWorld.addRigidBody(body);
return body;
}
Чтобы реализовать эффект разрушения, необходимо:
Отслеживать силу столкновения через contact callback.
При превышении порога силы:
Пример генерации новых тел:
function fractureCompoundBody(body) {
let compound = body.getCollisionShape();
let numChildren = compound.getNumChildShapes();
let transform = new Ammo.btTransform();
body.getMotionState().getWorldTransform(transform);
let origin = transform.getOrigin();
let rotation = transform.getRotation();
for (let i = 0; i < numChildren; i++) {
let childShape = compound.getChildShape(i);
let childTransform = compound.getChildTransform(i);
let worldTransform = new Ammo.btTransform();
worldTransform.setIdentity();
worldTransform.setOrigin(origin);
worldTransform.setRotation(rotation);
// Комбинируем трансформации родителя и дочернего shape
worldTransform.op_mul(childTransform);
let mass = 1;
let localInertia = new Ammo.btVector3(0, 0, 0);
childShape.calculateLocalInertia(mass, localInertia);
let motionState = new Ammo.btDefaultMotionState(worldTransform);
let rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, childShape, localInertia);
let newBody = new Ammo.btRigidBody(rbInfo);
physicsWorld.addRigidBody(newBody);
}
physicsWorld.removeRigidBody(body);
}
Ключевой аспект — точное перенесение текущей позиции и вращения родительского тела на дочерние фрагменты, чтобы избежать визуальных разрывов и нереалистичных телепортаций.
При работе с множеством фрагментов важно:
body.setActivationState(Ammo.DISABLE_DEACTIVATION);
Использование этих техник позволяет поддерживать стабильную производительность даже при большом количестве разрушаемых объектов.
Box, Sphere, Cylinder, Convex Hull — каждый тип shape имеет свои особенности:
Комбинирование разных типов shape в одном compound shape
позволяет создавать реалистичные и разнообразные разрушаемые
объекты.
Центр масс compound shape по умолчанию вычисляется
автоматически. Для корректного поведения при разрушении и падении можно
вручную смещать центр масс через setCenterOfMassTransform.
Это особенно важно для объектов с асимметричной структурой:
let centerTransform = new Ammo.btTransform();
centerTransform.setIdentity();
centerTransform.setOrigin(new Ammo.btVector3(0, -0.5, 0));
compoundShape.setCenterOfMassTransform(centerTransform);
Такой подход позволяет контролировать устойчивость объекта и реалистичное распределение веса при дроблении.