Получение нормали и позиции пересечения

В физическом движке Ammo.js, основанном на Bullet Physics, обработка столкновений и получение информации о точках пересечения требует работы с collision objects, rigid bodies и manifold points. Для точного определения нормалей и координат столкновений используется система manifold callbacks, которая позволяет получать детальные сведения о контактах между объектами.

Manifold и точки контакта

Каждое столкновение между двумя rigid body представлено через btPersistentManifold, который содержит список btManifoldPoint. Основные свойства btManifoldPoint:

  • getPositionWorldOnA() — возвращает координату точки контакта на объекте A в мировых координатах.
  • getPositionWorldOnB() — координата на объекте B.
  • getNormalWorldOnB() — вектор нормали на объекте B.
  • getDistance() — расстояние между точками контакта; отрицательное значение указывает на проникновение.

Для получения всех контактов в сцене используется dispatcher, который управляет списком всех столкновений:

let dispatcher = physicsWorld.getDispatcher();
let numManifolds = dispatcher.getNumManifolds();

for (let i = 0; i < numManifolds; i++) {
    let manifold = dispatcher.getManifoldByIndexInternal(i);
    let numContacts = manifold.getNumContacts();
    
    for (let j = 0; j < numContacts; j++) {
        let contactPoint = manifold.getContactPoint(j);
        let posA = contactPoint.getPositionWorldOnA();
        let posB = contactPoint.getPositionWorldOnB();
        let normalB = contactPoint.getNormalWorldOnB();
    }
}

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

Получение позиции пересечения

Для вычисления фактической точки пересечения между двумя объектами можно использовать среднее значение точек на объектах A и B:

let intersectionPos = new Ammo.btVector3(
    (posA.x() + posB.x()) / 2,
    (posA.y() + posB.y()) / 2,
    (posA.z() + posB.z()) / 2
);

Это даёт наиболее точное приближение реальной точки контакта, особенно при работе с сложными формами.

Работа с rayTest

Для обнаружения пересечений с помощью лучей используется rayTest или rayTestSingle:

let rayFrom = new Ammo.btVector3(0, 10, 0);
let rayTo = new Ammo.btVector3(0, 0, 0);

let rayCallback = new Ammo.ClosestRayResultCallback(rayFrom, rayTo);
physicsWorld.rayTest(rayFrom, rayTo, rayCallback);

if (rayCallback.hasHit()) {
    let hitPoint = rayCallback.get_hitPointWorld();
    let hitNormal = rayCallback.get_hitNormalWorld();
}

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

  • get_hitPointWorld() возвращает мировую координату пересечения луча с объектом.
  • get_hitNormalWorld() — нормаль поверхности в точке попадания.
  • Лучевой тест не учитывает глубину проникновения, поэтому для коллизий с физикой предпочтительно использовать manifold.

Преобразование в JavaScript объекты

Объекты типа btVector3 требуют ручного извлечения координат для работы в стандартных структурах Jav * aScript:

function vec3ToObj(btVec) {
    return { x: btVec.x(), y: btVec.y(), z: btVec.z() };
}

let intersection = vec3ToObj(intersectionPos);
let normal = vec3ToObj(normalB);

Это упрощает интеграцию с рендерингом и системой визуализации.

Системы фильтрации столкновений

Ammo.js позволяет фильтровать столкновения с помощью масок коллизий:

  • group — категория объекта.
  • mask — определяет, с какими группами может взаимодействовать объект.

Применение масок предотвращает обработку ненужных пересечений, повышая производительность, особенно при большом количестве объектов.

Практические рекомендации

  1. Всегда очищать btVector3 и btManifoldPoint после использования, чтобы избежать утечек памяти:
Ammo.destroy(intersectionPos);
  1. Для сложных форм предпочтительно использовать compound shapes, чтобы получать более точные нормали и позиции пересечений.
  2. При расчёте реакции на столкновение нормаль следует нормализовать перед применением силы:
let normalVec = new Ammo.btVector3(normalB.x(), normalB.y(), normalB.z());
normalVec.normalize();
  1. Для динамических объектов рекомендуется опираться на dispatcher.getNumManifolds() вместо глобальных коллизий, чтобы получать только активные контакты.

Итоговое использование

Комбинация manifold points и rayTest позволяет создавать системы детекции столкновений, точного позиционирования объектов и физического взаимодействия с высокой точностью. Правильная работа с нормалями и позициями пересечения является ключевым элементом для симуляции реалистичной физики, реализации реакций на столкновения, системы триггеров и сложных механик в играх и интерактивных приложениях.

Наличие всех этих инструментов делает Ammo.js мощным инструментом для обработки столкновений в браузерных проектах на JavaScript.