Преобразования между системами координат

Работа с физическим движком Ammo.js требует понимания двух основных систем координат: визуальной (например, Three.js) и физической (Bullet Physics, на котором основан Ammo.js). Визуальные объекты и физические тела используют разные представления позиции и ориентации, поэтому точное преобразование между ними критично для корректной симуляции.


Векторы и кватернионы

В Ammo.js позиции и скорости объектов задаются через класс btVector3, а ориентация — через btQuaternion. Эти классы предоставляют методы для чтения и записи координат:

let position = new Ammo.btVector3(0, 10, 0);
let rotation = new Ammo.btQuaternion(0, 0, 0, 1);
  • btVector3: хранит компоненты x, y, z.
  • btQuaternion: хранит компоненты x, y, z, w, где w — скалярная часть.

Визуальные движки часто используют объекты вроде THREE.Vector3 и THREE.Quaternion, что требует конвертации при синхронизации.


Преобразование позиции

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

function threeVectorToAmmo(v) {
    return new Ammo.btVector3(v.x, v.y, v.z);
}

function ammoVectorToThree(v) {
    return new THREE.Vector3(v.x(), v.y(), v.z());
}

Особенность Ammo.js: методы x(), y(), z() возвращают значения, но для изменения координат необходимо использовать setValue(x, y, z).

let physPos = new Ammo.btVector3();
physPos.setValue(1, 2, 3);

Преобразование ориентации

Кватернионы в Ammo.js и Three.js имеют одинаковую структуру (x, y, z, w), но важно учитывать порядок умножения и направление осей при наложении вращений:

function threeQuatToAmmo(q) {
    return new Ammo.btQuaternion(q.x, q.y, q.z, q.w);
}

function ammoQuatToThree(q) {
    return new THREE.Quaternion(q.x(), q.y(), q.z(), q.w());
}

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

let transform = body.getWorldTransform();
let origin = transform.getOrigin();
let rotation = transform.getRotation();

mesh.position.set(origin.x(), origin.y(), origin.z());
mesh.quaternion.set(rotation.x(), rotation.y(), rotation.z(), rotation.w());

Преобразования матриц

Ammo.js предоставляет объект btTransform, который хранит одновременно позицию и ориентацию. Из него можно извлекать матрицу для визуализации:

let transform = new Ammo.btTransform();
body.getMotionState().getWorldTransform(transform);

let origin = transform.getOrigin();
let rotation = transform.getRotation();

let matrix = new THREE.Matrix4();
matrix.compose(
    new THREE.Vector3(origin.x(), origin.y(), origin.z()),
    new THREE.Quaternion(rotation.x(), rotation.y(), rotation.z(), rotation.w()),
    new THREE.Vector3(1, 1, 1)
);
mesh.matrix = matrix;
mesh.matrixAutoUpdate = false;

Ключевой момент: btTransform всегда хранит мировые координаты тела относительно сцены физического движка.


Прямые и обратные преобразования

При работе с Ammo.js часто требуется:

  1. Прямое преобразование — из визуального объекта в физическое тело:
let t = new Ammo.btTransform();
t.setIdentity();
t.setOrigin(threeVectorToAmmo(mesh.position));
t.setRotation(threeQuatToAmmo(mesh.quaternion));
body.setWorldTransform(t);
  1. Обратное преобразование — после симуляции физики в визуальное представление:
body.getMotionState().getWorldTransform(t);
mesh.position.copy(ammoVectorToThree(t.getOrigin()));
mesh.quaternion.copy(ammoQuatToThree(t.getRotation()));

Использование setIdentity() важно, чтобы избежать накопления ошибок при последовательных преобразованиях.


Работа с локальными и мировыми координатами

Ammo.js различает локальные координаты тела и мировые координаты сцены. Методы getWorldTransform() и setWorldTransform() оперируют именно мировыми координатами. Для преобразования локальных координат относительно родительских тел можно использовать:

let localTransform = new Ammo.btTransform();
body.getMotionState().getWorldTransform(localTransform);

let parentTransform = parentBody.getWorldTransform();
localTransform.op_sub(parentTransform);

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


Применение при анимации и столкновениях

Для правильной анимации объектов и обработки столкновений:

  • Визуальная модель обновляется после шага симуляции.
  • Физическое тело получает целевые позиции и кватернионы для контролируемых объектов.
  • Преобразования между системами координат должны выполняться каждый кадр, чтобы избежать рассинхронизации.
physicsWorld.stepSimulation(deltaTime, 10);

bodies.forEach((body, i) => {
    let mesh = meshes[i];
    let t = new Ammo.btTransform();
    body.getMotionState().getWorldTransform(t);

    mesh.position.copy(ammoVectorToThree(t.getOrigin()));
    mesh.quaternion.copy(ammoQuatToThree(t.getRotation()));
});

Оптимизация преобразований

  • Избегать создания новых объектов btVector3 и btQuaternion каждый кадр — лучше использовать переиспользуемые экземпляры.
  • Использовать btTransform для групповых обновлений позиции и вращения.
  • Для статических тел обновления выполняются только при необходимости, что снижает нагрузку.

Особенности Bullet Physics в Ammo.js

  • Оси координат: X вправо, Y вверх, Z вперед. При переносе из других движков необходимо проверять совпадение осей.
  • Единицы измерения: Ammo.js ожидает метры, килограммы, секунды. Неправильные единицы приводят к некорректной физике.
  • Порядок применения вращений критичен: при умножении кватернионов q_new = q_delta * q_current направление вращения зависит от порядка.

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