btSerializer: сохранение и загрузка физического мира

Библиотека Ammo.js предоставляет мощный инструмент для работы с физическими симуляциями в браузере на базе движка Bullet Physics. Одной из ключевых возможностей для сложных проектов является использование btSerializer — механизма сохранения и загрузки состояния физического мира. Это позволяет сохранять сцену, пересоздавать объекты и восстанавливать их состояние без повторного вычисления начальных условий.


Основные концепции

btSerializer работает с внутренним форматом Bullet Physics, поддерживая сохранение:

  • Collision Shapes — геометрические формы объектов, включая сферы, коробки, капсулы, конусы, полигоны и композитные структуры.
  • Rigid Bodies — твердые тела с массой, инерцией, позицией, ориентацией и линейной/угловой скоростью.
  • Constraints — ограничения и соединения между объектами (hinge, slider, cone twist и другие).
  • Dynamics World — глобальное состояние симуляции: гравитация, шаг интеграции, параметры столкновений.

Файл, созданный btSerializer, можно рассматривать как «снимок» текущей физической сцены, который может быть сериализован в массив байт или строку для последующей загрузки.


Создание сериализатора

Для сериализации требуется экземпляр btDefaultSerializer:

const serializer = new Ammo.btDefaultSerializer();

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


Сериализация мира

Сохранение состояния мира осуществляется через serialize объекта btDiscreteDynamicsWorld:

const world = new Ammo.btDiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration);

// заполнение мира rigid bodies и collision shapes

world.serialize(serializer);

После этого сериализатор содержит бинарное представление мира. Для дальнейшего использования данные можно получить через:

const bufferPointer = serializer.getBufferPointer();
const bufferSize = serializer.getCurrentBufferSize();
const arrayBuffer = new Uint8Array(bufferPointer, bufferSize);

Эти данные можно сохранять в IndexedDB, LocalStorage, или отправлять на сервер для долговременного хранения.


Десериализация и восстановление

Для восстановления физического мира из сериализованных данных используется btDefaultSerializer вместе с функцией десериализации:

const arrayBuffer = /* данные из сохранения */;
const data = Ammo._malloc(arrayBuffer.length);
Ammo.HEAPU8.set(arrayBuffer, data);

const world = new Ammo.btDiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration);
const serializer = new Ammo.btDefaultSerializer();
serializer.setBufferPointer(data, arrayBuffer.length);

Ammo.deserializeBullet(serializer, world);

В результате восстанавливаются все rigid bodies, collision shapes и constraints, а также их позиции и скорости. Это позволяет мгновенно возобновить симуляцию с точностью до миллисекунды.


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

  1. Сложные объекты: btSerializer корректно обрабатывает compound shapes и сцены с множеством динамических тел.
  2. Constraints: все типы ограничений сохраняются, включая их параметры и локальные матрицы.
  3. Иерархия объектов: сериализация сохраняет связи между телами и shapes, что важно для мультикомпонентных объектов.
  4. Размер данных: бинарный формат компактнее JSON-представления и позволяет экономить память и время передачи.
  5. Совместимость версий: при обновлении Ammo.js или Bullet Physics необходимо учитывать возможные изменения в формате сериализации.

Практические советы

  • Сериализация лучше всего работает после одного или нескольких шагов симуляции — так фиксируются реальные позиции и скорости объектов.
  • Для больших сцен можно применять incremental serialization, сохраняя только изменившиеся объекты, чтобы уменьшить нагрузку на память.
  • Для отладки полезно комбинировать btSerializer с визуализацией collision shapes через Three.js или Babylon.js, чтобы убедиться, что восстановление прошло корректно.
  • Перед десериализацией всегда создается чистый мир с правильной конфигурацией collisionConfiguration, dispatcher и solver, иначе восстановление будет некорректным.

Интеграция с игровыми движками

В игровых проектах btSerializer используется для:

  • Сохранения прогресса игрока с физически активными объектами.
  • Создания предзагруженных сцен с расставленными динамическими объектами.
  • Передачи состояния между клиентами и сервером для сетевой синхронизации.

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


Совместное использование с другими функциями Ammo.js

  • btDefaultMotionState: при восстановлении тел корректно восстанавливается их положение и ориентация.
  • btTransform: обеспечивает точное восстановление локальных и глобальных координат.
  • btCompoundShape: сохраняются все дочерние формы, включая смещения и вращения относительно родителя.

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


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