Синхронизация mesh Three.js с телом через MotionState

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

MotionState реализуется через объект с методами getWorldTransform и setWorldTransform. getWorldTransform вызывается движком физики для получения текущей трансформации тела, а setWorldTransform используется для передачи обновленной позиции и ориентации тела обратно в Three.js mesh.

class MotionState {
    constructor(mesh) {
        this.mesh = mesh;
        this.transform = new Ammo.btTransform();
    }

    getWorldTransform(worldTrans) {
        worldTrans.setOrigin(this.transform.getOrigin());
        worldTrans.setRotation(this.transform.getRotation());
    }

    setWorldTransform(worldTrans) {
        const origin = worldTrans.getOrigin();
        const rotation = worldTrans.getRotation();
        this.mesh.position.set(origin.x(), origin.y(), origin.z());
        this.mesh.quaternion.set(rotation.x(), rotation.y(), rotation.z(), rotation.w());
    }
}

В этом примере mesh.position и mesh.quaternion синхронизируются с физическим телом, обеспечивая точное отображение движка физики в Three.js.


Создание rigid body с MotionState

Rigid body в Ammo.js требует описания формы, массы и начальной трансформации. MotionState интегрируется через конструктор btRigidBody. Основные шаги:

  1. Создание трансформации:
const transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(0, 10, 0));
transform.setRotation(new Ammo.btQuaternion(0, 0, 0, 1));
  1. Определение инерции (если масса > 0):
const mass = 1;
const localInertia = new Ammo.btVector3(0, 0, 0);
shape.calculateLocalInertia(mass, localInertia);
  1. Создание MotionState и rigid body:
const motionState = new MotionState(mesh);
const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, shape, localInertia);
const body = new Ammo.btRigidBody(rbInfo);
physicsWorld.addRigidBody(body);

Использование MotionState гарантирует, что физическая симуляция и визуальное представление будут оставаться синхронизированными без необходимости вручную копировать позиции на каждом кадре.


Обновление визуального объекта через MotionState

При каждом обновлении мира физики Ammo.js вызывает метод setWorldTransform MotionState, что позволяет напрямую обновлять свойства Three.js объекта. Это значительно снижает нагрузку по сравнению с ручным чтением позиции и ориентации rigid body.

Пример интеграции с циклом рендеринга Three.js:

function animate() {
    requestAnimationFrame(animate);
    physicsWorld.stepSimulation(1 / 60, 10);

    // Обновление всех объектов происходит автоматически через MotionState
    renderer.render(scene, camera);
}

Особенности работы с Quaternion и Matrix

MotionState работает напрямую с btTransform, который содержит btVector3 для позиции и btQuaternion для ориентации. При работе с Three.js quaternion используется для установки ориентации mesh. Важно правильно преобразовывать форматы:

const origin = transform.getOrigin();
const rotation = transform.getRotation();
mesh.position.set(origin.x(), origin.y(), origin.z());
mesh.quaternion.set(rotation.x(), rotation.y(), rotation.z(), rotation.w());

Использование матриц трансформации напрямую (mesh.matrix) возможно, но рекомендуется работать через позицию и quaternion для лучшей совместимости с Three.js и анимациями.


Советы по оптимизации

  • Минимизация объектов Ammo.btVector3 и Ammo.btQuaternion: повторное использование объектов вместо создания новых каждый кадр снижает нагрузку на сборщик мусора.
  • Интеграция с InstancedMesh: MotionState может быть расширен для работы с несколькими экземплярами, передавая индексы в массивы позиции и quaternion.
  • Обновление только при изменении: если rigid body спит или неподвижен, можно временно приостанавливать обновление визуального объекта.

Расширенные возможности

MotionState можно расширять для поддержки:

  • Событий столкновения, передавая информацию о коллизии mesh.
  • Анимации ragdoll, где каждый сегмент скелета имеет отдельный MotionState и физическое тело.
  • Смешанных систем, где одни объекты управляются физикой, а другие анимацией или скриптами, с плавным переходом через MotionState.

Применение MotionState позволяет создавать реалистичные физические симуляции, синхронизированные с визуальными объектами Three.js, обеспечивая точность и высокую производительность.