В контексте физического движка Ammo.js критически важно правильно
работать с трансформами объектов, чтобы избежать лишних аллокаций памяти
и повышенной нагрузки на сборщик мусора. Каждый физический объект (rigid
body) содержит объект btRigidBody, который имеет свой
трансформ btTransform, представляющий позицию и ориентацию
объекта в мировых координатах.
Основные методы работы с трансформами:
getWorldTransform() — возвращает текущий мировой
трансформ тела.setWorldTransform(btTransform) — задаёт новый мировой
трансформ.Ключевой момент заключается в том, что каждый вызов
new btTransform() создаёт новый объект в памяти. При частом
обновлении трансформаций это приводит к интенсивной работе сборщика
мусора и падению производительности.
btTransform повторноЧтобы избежать лишних аллокаций, рекомендуется создавать объект
btTransform один раз и повторно использовать его для чтения
и записи трансформов. Например:
const tmpTransform = new Ammo.btTransform();
// чтение трансформа тела
rigidBody.getMotionState().getWorldTransform(tmpTransform);
// модификация позиции
const origin = tmpTransform.getOrigin();
origin.setValue(x, y, z);
// установка обратно в тело
rigidBody.getMotionState().setWorldTransform(tmpTransform);
rigidBody.setWorldTransform(tmpTransform);
Здесь создаётся один объект
tmpTransform, который используется для всех
операций, что предотвращает постоянное выделение памяти.
При обновлении сцены важно различать операции чтения и записи. Часто
нужно только читать текущую позицию для визуализации, а не изменять
физику объекта. В таких случаях рекомендуется использовать отдельный
временный btTransform для чтения:
const tmpReadTransform = new Ammo.btTransform();
rigidBody.getMotionState().getWorldTransform(tmpReadTransform);
const pos = tmpReadTransform.getOrigin();
const quat = tmpReadTransform.getRotation();
При этом объект для записи (tmpTransform) остаётся
неизменным, что упрощает управление памятью и предотвращает
непреднамеренные побочные эффекты.
btRigidBodyИногда требуется обновить положение тела без использования
MotionState. В этом случае можно напрямую работать с
btRigidBody:
tmpTransform.setOrigin(new Ammo.btVector3(x, y, z));
tmpTransform.setRotation(new Ammo.btQuaternion(qx, qy, qz, qw));
rigidBody.setWorldTransform(tmpTransform);
Важно помнить, что btVector3 и btQuaternion
также являются объектами, и их постоянное создание приведёт к
аллокациям. Для оптимизации следует заранее создавать их и
переиспользовать.
const tmpOrigin = new Ammo.btVector3();
const tmpQuat = new Ammo.btQuaternion();
// в цикле обновления
tmpOrigin.setValue(x, y, z);
tmpQuat.setValue(qx, qy, qz, qw);
tmpTransform.setOrigin(tmpOrigin);
tmpTransform.setRotation(tmpQuat);
rigidBody.setWorldTransform(tmpTransform);
Для синхронизации физики с рендером рекомендуется использовать одну
структуру данных для хранения трансформов. Вместо того чтобы создавать
новые объекты Vector3 или Quaternion на каждый
кадр, можно обновлять существующие:
const meshPosition = { x: 0, y: 0, z: 0 };
const meshQuaternion = { x: 0, y: 0, z: 0, w: 1 };
rigidBody.getMotionState().getWorldTransform(tmpTransform);
const origin = tmpTransform.getOrigin();
meshPosition.x = origin.x();
meshPosition.y = origin.y();
meshPosition.z = origin.z();
const rotation = tmpTransform.getRotation();
meshQuaternion.x = rotation.x();
meshQuaternion.y = rotation.y();
meshQuaternion.z = rotation.z();
meshQuaternion.w = rotation.w();
Такой подход позволяет избежать создания промежуточных объектов и снижает нагрузку на сборщик мусора при высокочастотной анимации.
btVector3 и
btQuaternion внутри циклов. Локальные объекты
должны быть постоянными и переиспользуемыми.new btTransform() каждый
кадр, это ключевой источник утечек и тормозов при большом
количестве объектов.Эти техники позволяют максимально эффективно передавать трансформы между физикой и визуализацией, сохраняя производительность и избегая лишней нагрузки на память.