Ammo.js — это порт библиотеки Bullet Physics на
JavaScript, обеспечивающий реалистичную физику твёрдых тел и мягких тел
в браузере. Одной из часто используемых возможностей является создание
динамических объектов типа верёвки или цепочки, которые реагируют на
силы, столкновения и гравитацию. Для этого используется функция
CreateRope, позволяющая моделировать цепочки тел с
ограничениями между ними.
Верёвка в Ammo.js моделируется как система
связанных твёрдых тел, где каждый сегмент является отдельным
телом, а соединение между сегментами обеспечивается через
constraints — ограничения движения. Это позволяет
верёвке:
Ключевые элементы модели:
RigidBody) —
цилиндры или коробки, которые визуально образуют куски верёвки.btPoint2PointConstraint
или btGeneric6DofConstraint) — определяют соединения между
сегментами и обеспечивают гибкость и стабильность.Каждый сегмент создаётся как отдельное твёрдое тело. Наиболее распространённые шаги:
let segmentMass = 0.1; // маленькая масса для гибкости
let segmentLength = 0.2; // длина одного сегмента
let segmentRadius = 0.05; // радиус цилиндра
let shape = new Ammo.btCylinderShape(new Ammo.btVector3(segmentRadius, segmentLength/2, segmentRadius));
let transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(x, y, z)); // позиция сегмента
let motionState = new Ammo.btDefaultMotionState(transform);
let localInertia = new Ammo.btVector3(0, 0, 0);
shape.calculateLocalInertia(segmentMass, localInertia);
let rbInfo = new Ammo.btRigidBodyConstructionInfo(segmentMass, motionState, shape, localInertia);
let body = new Ammo.btRigidBody(rbInfo);
physicsWorld.addRigidBody(body);
Особенности:
btCylinderShape или
btBoxShape для упрощённой коллизии.Для реализации гибкой цепочки применяется
btPoint2PointConstraint. Оно создаёт точку соединения между
двумя телами:
let pivotA = new Ammo.btVector3(0, -segmentLength/2, 0);
let pivotB = new Ammo.btVector3(0, segmentLength/2, 0);
let constraint = new Ammo.btPoint2PointConstraint(bodyA, bodyB, pivotA, pivotB);
physicsWorld.addConstraint(constraint, true);
Пояснение:
pivotA и pivotB задают локальные точки
соединения относительно центров тел.addConstraint (true)
включает активацию ограничения в симуляции.Создание длинной верёвки требует генерации массива сегментов и последовательного соединения их ограничениями:
let ropeSegments = [];
let numSegments = 20;
for (let i = 0; i < numSegments; i++) {
let body = createSegment(startX, startY - i * segmentLength, startZ);
ropeSegments.push(body);
if (i > 0) {
let constraint = new Ammo.btPoint2PointConstraint(
ropeSegments[i-1],
body,
new Ammo.btVector3(0, -segmentLength/2, 0),
new Ammo.btVector3(0, segmentLength/2, 0)
);
physicsWorld.addConstraint(constraint, true);
}
}
Важные моменты:
mass = 0).setCollisionFlags) для повышения
производительности.btGeneric6DofConstraint
— позволяет ограничить угол изгиба между сегментами, предотвращая
чрезмерное скручивание.Для интеграции с рендерингом, например с Three.js, каждый сегмент связывается с 3D объектом:
let mesh = new THREE.Mesh(
new THREE.CylinderGeometry(segmentRadius, segmentRadius, segmentLength, 8),
new THREE.MeshStandardMaterial({ color: 0x888888 })
);
scene.add(mesh);
body.threeMesh = mesh;
Обновление позиции происходит после шага симуляции:
function updateMeshes() {
for (let body of ropeSegments) {
let transform = new Ammo.btTransform();
body.getMotionState().getWorldTransform(transform);
let origin = transform.getOrigin();
let rotation = transform.getRotation();
body.threeMesh.position.set(origin.x(), origin.y(), origin.z());
body.threeMesh.quaternion.set(rotation.x(), rotation.y(), rotation.z(), rotation.w());
}
}
btSoftBody и
сегментов создаёт ещё более плавное поведение.btHingeConstraint) позволяет контролировать
кручение.Для длинных верёвок (более 30 сегментов) критично:
btGeneric6DofConstraint для предотвращения чрезмерного
перекручивания.Эти методы обеспечивают высокую стабильность и реалистичное поведение верёвки в любых сценариях, от подвешенных объектов до динамических симуляций с воздействием внешних сил.