В Oimo.js кватернионы используются для представления ориентации объектов в трехмерном пространстве. В отличие от углов Эйлера, кватернионы позволяют избегать эффекта гимбал-лок (gimbal lock) и обеспечивают плавное вращение без скачков.
Кватернион представляется как четырёхкомпонентный вектор ((x, y, z,
w)), где первые три компонента определяют вектор вращения, а последняя —
скалярная часть. В Oimo.js объекты типа RigidBody имеют
свойства position и rotation, где
rotation хранится как кватернион.
let body = world.add({
type: 'box',
size: [1, 1, 1],
pos: [0, 5, 0],
rot: [0, 0, 0, 1], // единичный кватернион
});
Единичный кватернион ([0, 0, 0, 1]) соответствует отсутствию вращения.
Синхронизация кватернионов необходима для случаев, когда несколько объектов должны иметь согласованную ориентацию. Типичный пример — соединенные механизмы или визуальные объекты, которые должны повторять физическое тело.
Oimo.js хранит ориентацию тела в body.rotation
(кватернион) и предоставляет методы для обновления кватерниона
вручную:
body.rotation.set(x, y, z, w);
Для плавной синхронизации часто используют интерполяцию кватернионов (slerp — spherical linear interpolation). Она обеспечивает равномерное вращение между двумя ориентациями:
function slerp(q1, q2, t) {
let cosTheta = q1.dot(q2);
if (cosTheta < 0) {
q2 = q2.clone().negate();
cosTheta = -cosTheta;
}
let angle = Math.acos(cosTheta);
if (angle < 0.001) return q1.clone();
let sinAngle = Math.sin(angle);
let w1 = Math.sin((1 - t) * angle) / sinAngle;
let w2 = Math.sin(t * angle) / sinAngle;
return q1.clone().multiplyScalar(w1).add(q2.clone().multiplyScalar(w2));
}
Использование SLERP позволяет плавно обновлять ориентацию визуальной модели на основе физического тела:
let targetQuat = body.rotation;
mesh.quaternion.copy(slerp(mesh.quaternion, targetQuat, 0.1));
В 3D-сценах чаще всего физическое тело RigidBody
управляет движением, а графический объект (например, Three.js
Mesh) синхронизируется с его кватернионом:
mesh.position.copy(body.position);
mesh.quaternion.copy(body.rotation);
Если требуется более сложная синхронизация, например с локальными смещениями, используют композицию кватернионов. Для локального вращения относительно тела:
let localQuat = new THREE.Quaternion();
localQuat.setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI/4);
mesh.quaternion.copy(body.rotation).multiply(localQuat);
Такое умножение гарантирует, что локальное вращение применяется после ориентации физического тела.
Физический движок Oimo.js рассчитывает новые кватернионы при
применении силы или момента. После каждого шага симуляции кватернион
тела обновляется автоматически. Для контроля можно считывать его в
событии world.step():
world.step();
let currentQuat = body.rotation.clone();
В случаях, когда требуется влияние внешних факторов на ориентацию (например, пользовательский ввод или анимация), кватернион можно комбинировать с текущим:
let deltaQuat = new OIMO.Quat();
deltaQuat.setFromAxisAngle(new OIMO.Vec3(0,1,0), deltaAngle);
body.rotation.mul(deltaQuat);
Это позволяет добавлять вращение к существующей ориентации без разрыва непрерывности движения.
quat.normalize()let copy = quat.clone()quat.invert()quat.mul(otherQuat)quat.setFromEuler(x, y, z)Эти функции позволяют строить сложные механизмы синхронизации и анимации без потери точности и стабильности.