Обработка масштабирования и Babylon-координат

Oimo.js — это высокопроизводительная JavaScript-библиотека для симуляции физики твёрдых тел, реализующая динамическое моделирование тел с использованием метода жёстких тел (rigid body). Основной строительный блок — мир (world), который содержит все объекты, силы, коллайдеры и шаги симуляции.

Создание мира выполняется через:

let world = new OIMO.World({
    timestep: 1/60,
    iterations: 8,
    broadphase: 2, // 1: brute-force, 2: sweep & prune
    worldscale: 1, // масштаб мира
    random: true,
    info: false
});

Ключевые параметры:

  • timestep — шаг интеграции физики, обычно 1/60 секунды для 60 FPS.
  • iterations — количество итераций при расчёте контактных сил, влияет на стабильность.
  • broadphase — алгоритм широкого поиска коллизий; 2 обеспечивает баланс скорости и точности.
  • worldscale — коэффициент масштабирования физических координат относительно визуальных. Это критически важно при работе с Babylon.js, где единицы сцены могут отличаться.

Масштабирование физических тел

В Oimo.js все размеры и силы напрямую зависят от масштаба мира. Неправильное соответствие между координатами визуализации и физическим миром приводит к некорректной симуляции: объекты «проникают» друг в друга или слишком резко реагируют на силы.

Принципы масштабирования:

  1. Единый коэффициент — все размеры тел, силы гравитации, скорости и массы должны быть пропорционально масштабированы.
  2. Коррекция силы тяжести — если сцена визуализируется в метрах, а физический мир в сантиметрах (worldscale = 0.01), гравитацию необходимо умножать на обратный коэффициент.
  3. Диапазон стабильности — оптимальный масштаб для Oimo.js обычно находится в пределах 0.1–10 единиц. Объекты слишком маленькие или слишком большие могут вести себя нестабильно.

Пример создания куба с учётом масштабирования:

let size = 2; // размер куба в визуальных единицах
let scaledSize = size * world.worldscale;

let box = world.add({
    type: "box",
    size: [scaledSize, scaledSize, scaledSize],
    pos: [0, 5*world.worldscale, 0],
    rot: [0, 0, 0],
    move: true,
    density: 1
});

Babylon.js координаты и конвертация

Babylon.js использует правую систему координат (Y вверх), а Oimo.js — также правую систему, но с возможными различиями в единицах и масштабе. Основные моменты синхронизации:

  • Позиция: mesh.position в Babylon.js должна умножаться на worldscale при установке в Oimo.js, и наоборот при обновлении визуала из физики.
  • Ротация: Oimo.js возвращает вращение в виде матрицы или кватерниона, которые нужно преобразовать в BABYLON.Quaternion для Babylon.js:
mesh.position.copyFrom(box.getPosition().scale(1 / world.worldscale));
mesh.rotationQuaternion.copyFrom(box.getQuaternion());
  • Скорости и силы: также требуют масштабирования по worldscale. Линейная скорость умножается на worldscale, угловая скорость — на worldscale или её обратное значение в зависимости от единиц.

Контроль стабильности при масштабировании

  • Малые объекты (<0.1 в масштабе мира) склонны к дрожанию и проскакиванию через поверхности. Решение — увеличить worldscale или уменьшить timestep.
  • Большие объекты (>10) могут падать слишком быстро, требуя увеличения iterations и корректировки gravity.
  • Плавное взаимодействие с Babylon.js достигается через интерполяцию визуальной позиции между физическими шагами. Это минимизирует «рывки» при несинхронной частоте рендеринга и физики:
mesh.position.lerp(box.getPosition().scale(1/world.worldscale), 0.5);
mesh.rotationQuaternion.slerp(box.getQuaternion(), 0.5);

Практика работы с комплексными объектами

Для сложных тел из нескольких частей (compound shapes):

  1. Создаются отдельные коллайдеры с соответствующим масштабом.
  2. Все части объединяются через OIMO.Body с позиционным смещением.
  3. Масштабирование применяется к каждой части индивидуально, чтобы сохранить точность физики.
let compound = world.add({
    type: "compound",
    move: true
});

compound.addBox([1*world.worldscale, 1*world.worldscale, 1*world.worldscale], [0,0,0]);
compound.addBox([0.5*world.worldscale, 0.5*world.worldscale, 2*world.worldscale], [0,1,0]);

Взаимодействие с Babylon.js: синхронизация сцен

  • Обновление в анимационном цикле:
engine.runRenderLoop(() => {
    world.step();

    for (let obj of objects) {
        obj.mesh.position.copyFrom(obj.body.getPosition().scale(1/world.worldscale));
        obj.mesh.rotationQuaternion.copyFrom(obj.body.getQuaternion());
    }

    scene.render();
});
  • Поддержка разных частот: если физика и рендер имеют разную частоту, вводится буферная интерполяция или использование фиксированного timestep с множественными шагами на кадр.

Влияние массы и плотности

Масса определяется через плотность и объём объекта. При масштабировании важно:

  • Масса должна масштабироваться как куб коэффициента размеров (mass ∝ scale³).
  • Плотность (density) подбирается для сохранения реалистичной динамики при изменении размеров.

Закрепление практических правил

  • Всегда проверять соответствие единиц визуализации и физики.
  • Использовать worldscale для корректной передачи размеров и сил.
  • Масштабировать все физические параметры пропорционально.
  • Применять интерполяцию для плавного отображения движений.
  • Для сложных объектов работать с составными телами, корректируя позицию и размер каждой части.

Эти подходы позволяют строить точные и стабильные симуляции в Babylon.js с использованием Oimo.js, учитывая масштабирование и преобразование координат.