Библиотека Ammo.js представляет собой порт популярного движка физики Bullet Physics на JavaScript с использованием WebAssembly. Она обеспечивает реалистичное моделирование твердых тел, столкновений и динамики объектов. Интеграция с Three.js позволяет визуализировать физические объекты в 3D-сцене, связывая физические тела с графическими мешами.
Ключевым элементом взаимодействия является синхронизация
векторов позиции и вращения между Ammo.js и Three.js
через объекты Vector3 и Quaternion. В
Ammo.js все расчеты выполняются в собственной системе типов
(btVector3, btQuaternion), в то время как
Three.js использует свои THREE.Vector3 и
THREE.Quaternion.
В Ammo.js позиция объекта хранится в виде
btVector3, который представляет собой
трехкомпонентный вектор (x, y, z). Для создания нового вектора:
const positionAmmo = new Ammo.btVector3(0, 10, 0);
Для получения значения вектора для Three.js необходимо извлечь компоненты:
const positionThree = new THREE.Vector3(
positionAmmo.x(),
positionAmmo.y(),
positionAmmo.z()
);
Обратное преобразование требуется для установки позиции физического тела из графического объекта:
const newPositionAmmo = new Ammo.btVector3(positionThree.x, positionThree.y, positionThree.z);
rigidBody.setWorldTransform(new Ammo.btTransform().setOrigin(newPositionAmmo));
Важно помнить, что btVector3 является объектом
WebAssembly, и для больших сцен частое создание новых объектов
может привести к утечкам памяти. Оптимальным подходом является
переиспользование существующих векторов и очистка их через
Ammo.destroy().
Для задания ориентации объекта в Ammo.js используется
btQuaternion. Кватернион описывает
вращение без использования углов Эйлера, что предотвращает проблемы типа
гимбального замка.
Создание кватерниона:
const rotationAmmo = new Ammo.btQuaternion(0, 0, 0, 1); // единичный кватернион
Получение данных для Three.js:
const rotationThree = new THREE.Quaternion(
rotationAmmo.x(),
rotationAmmo.y(),
rotationAmmo.z(),
rotationAmmo.w()
);
Для установки вращения физического объекта:
const newRotationAmmo = new Ammo.btQuaternion(
rotationThree.x,
rotationThree.y,
rotationThree.z,
rotationThree.w
);
const transform = new Ammo.btTransform();
transform.setRotation(newRotationAmmo);
rigidBody.setWorldTransform(transform);
Синхронизация кватернионов особенно важна при работе с динамическими телами, которые вращаются под воздействием физических сил и импульсов.
Типичная интеграция Ammo.js с Three.js включает цикл обновления, где положение и вращение физического тела обновляются в графическом меше:
function updatePhysics(deltaTime) {
physicsWorld.stepSimulation(deltaTime, 10);
for (let i = 0; i < rigidBodies.length; i++) {
const objThree = rigidBodies[i].mesh;
const objAmmo = rigidBodies[i].body;
const motionState = objAmmo.getMotionState();
if (motionState) {
const tmpTransform = new Ammo.btTransform();
motionState.getWorldTransform(tmpTransform);
const origin = tmpTransform.getOrigin();
const rotation = tmpTransform.getRotation();
objThree.position.set(origin.x(), origin.y(), origin.z());
objThree.quaternion.set(rotation.x(), rotation.y(), rotation.z(), rotation.w());
}
}
}
Здесь stepSimulation выполняет физическое обновление, а
затем происходит извлечение позиции и ориентации для визуализации. Такой
подход обеспечивает реалистичное движение объектов,
включая столкновения, падения и вращение.
Ammo.js требует установки начальной позиции и
ориентации при создании тела. Используется объект
btTransform, который объединяет btVector3 и
btQuaternion:
const transform = new Ammo.btTransform();
transform.setIdentity(); // сброс к стандартному положению
transform.setOrigin(new Ammo.btVector3(x, y, z));
transform.setRotation(new Ammo.btQuaternion(qx, qy, qz, qw));
const motionState = new Ammo.btDefaultMotionState(transform);
const mass = 1;
const localInertia = new Ammo.btVector3(0, 0, 0);
shape.calculateLocalInertia(mass, localInertia);
const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, shape, localInertia);
const body = new Ammo.btRigidBody(rbInfo);
physicsWorld.addRigidBody(body);
Такой подход позволяет точно контролировать начальные условия, что особенно важно для симуляций, где важны точность столкновений и правильное вращение.
Ammo.destroy().stepSimulation, чтобы визуализация точно отражала
физику.const boxGeometry = new THREE.BoxGeometry(1, 1, 1);
const boxMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial);
scene.add(boxMesh);
const boxShape = new Ammo.btBoxShape(new Ammo.btVector3(0.5, 0.5, 0.5));
const startTransform = new Ammo.btTransform();
startTransform.setIdentity();
startTransform.setOrigin(new Ammo.btVector3(0, 5, 0));
startTransform.setRotation(new Ammo.btQuaternion(0, 0, 0, 1));
const motionState = new Ammo.btDefaultMotionState(startTransform);
const mass = 1;
const localInertia = new Ammo.btVector3(0, 0, 0);
boxShape.calculateLocalInertia(mass, localInertia);
const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, boxShape, localInertia);
const rigidBody = new Ammo.btRigidBody(rbInfo);
physicsWorld.addRigidBody(rigidBody);
rigidBodies.push({ mesh: boxMesh, body: rigidBody });
В этом примере создается твердое тело, которое подчиняется физическим законам, и его состояние постоянно синхронизируется с Three.js мешем через позицию и кватернион. Это обеспечивает правильное отображение динамических взаимодействий между объектами сцены и физическим миром.