Синхронизация вершин сетки Three.js с узлами мягкого тела

Ammo.js — это порт Bullet Physics на JavaScript с использованием WebAssembly. Для работы с мягкими телами необходима правильная инициализация физического мира. Сначала создаётся объект Ammo.btSoftBodyWorldInfo, который хранит глобальные параметры мягких тел:

const collisionConfiguration = new Ammo.btSoftBodyRigidBodyCollisionConfiguration();
const dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
const broadphase = new Ammo.btDbvtBroadphase();
const solver = new Ammo.btSequentialImpulseConstraintSolver();
const softBodySolver = new Ammo.btDefaultSoftBodySolver();

const physicsWorld = new Ammo.btSoftRigidDynamicsWorld(
    dispatcher,
    broadphase,
    solver,
    collisionConfiguration,
    softBodySolver
);

physicsWorld.setGravity(new Ammo.btVector3(0, -9.81, 0));
const softBodyWorldInfo = new Ammo.btSoftBodyWorldInfo();
softBodyWorldInfo.set_m_gravity(new Ammo.btVector3(0, -9.81, 0));
softBodyWorldInfo.set_m_sparsesdf(new Ammo.btSparseSdf3());

Ключевым моментом является установка m_gravity и m_sparsesdf, которые управляют поведением мягких тел.


Создание мягкого тела

Мягкое тело создаётся на основе сетки вершин (mesh) в Three.js. Наиболее часто используется функция btSoftBodyHelpers.CreateFromTriMesh, которая принимает массив вершин и индексов треугольников:

const vertices = geometry.attributes.position.array;
const indices = geometry.index.array;
const softBody = Ammo.btSoftBodyHelpers.CreateFromTriMesh(
    softBodyWorldInfo,
    vertices,
    indices,
    indices.length / 3,
    true
);

Основные параметры:

  • softBodyWorldInfo — информация о физическом мире для мягких тел.
  • vertices — массив координат вершин в формате [x1, y1, z1, x2, y2, z2, ...].
  • indices — массив индексов треугольников.
  • true в конце — включает генерацию нормалей для столкновений.

После создания мягкое тело необходимо добавить в мир:

physicsWorld.addSoftBody(softBody);
softBody.setTotalMass(1, false);
softBody.setPose(true, false);

Настройка параметров мягкого тела

Для реалистичного поведения важно задать свойства материала:

const sbConfig = softBody.get_m_cfg();
sbConfig.set_viterations(40); // внутренние итерации
sbConfig.set_piterations(40); // позиционные итерации
sbConfig.set_kDF(0.5);        // демпфирование
sbConfig.set_kDP(0.01);       // динамическое демпфирование
sbConfig.set_kPR(250);        // жёсткость растяжения
  • viterations и piterations определяют стабильность симуляции.
  • kDF и kDP отвечают за гашение колебаний.
  • kPR регулирует сопротивление растяжению.

Эти параметры напрямую влияют на поведение вершин сетки.


Синхронизация вершин Three.js с узлами мягкого тела

Каждая вершина geometry в Three.js должна обновляться согласно положению соответствующего узла мягкого тела в Ammo.js. Получение узлов выполняется через:

const nodes = softBody.get_m_nodes();
for (let i = 0; i < nodes.size(); i++) {
    const node = nodes.at(i);
    const pos = node.get_m_x();
    vertices[3*i]     = pos.x();
    vertices[3*i + 1] = pos.y();
    vertices[3*i + 2] = pos.z();
}
geometry.attributes.position.needsUpdate = true;
geometry.computeVertexNormals();

Ключевые моменты:

  • Индексы узлов соответствуют индексам вершин в исходном массиве.
  • После обновления массива position обязательно устанавливать needsUpdate = true.
  • Пересчёт нормалей обеспечивает корректное освещение.

Оптимизация производительности

  1. Использование Float32Array для вершин — минимизирует лишние копирования данных.
  2. Снижение количества итераций (viterations, piterations) при малых или медленных объектах.
  3. Обновление нормалей не каждый кадр, если деформация малозаметна.
  4. Разделение больших мягких тел на несколько меньших для параллельной обработки.

Взаимодействие с другими объектами

Мягкие тела в Ammo.js могут взаимодействовать с твёрдыми объектами. Для этого твёрдое тело добавляется в тот же physicsWorld:

const rigidBody = createRigidBody(mesh, mass);
physicsWorld.addRigidBody(rigidBody);
softBody.appendAnchor(nodeIndex, rigidBody, false, 1);
  • appendAnchor позволяет закрепить узел к твёрдому телу с определённой силой.
  • Опция false отключает ограничение вращения.

Такой подход позволяет создавать тканевые сцены с объектами, на которые они могут реагировать физически.


Обновление симуляции в анимационном цикле

Для корректного движения мягкого тела шаг симуляции должен вызываться на каждом кадре:

const deltaTime = clock.getDelta();
physicsWorld.stepSimulation(deltaTime, 10);
updateSoftBodyMesh(softBody, geometry);
renderer.render(scene, camera);
  • stepSimulation принимает deltaTime и максимальное число суб-итераций.
  • Функция updateSoftBodyMesh синхронизирует вершины Three.js с узлами мягкого тела.
  • Такая организация обеспечивает плавное движение и стабильное поведение.

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