Oimo.js — это высокопроизводительная физическая библиотека для JavaScript, реализующая твердые тела с динамикой столкновений и симуляцией физических законов. Основной принцип работы основан на динамическом интегрировании и решении ограничений через систему тел и суставов. Библиотека позволяет создавать сцены с множеством объектов, поддерживая как статические, так и динамические тела, а также взаимодействия между ними.
В основе лежат следующие концепции:
import * as OIMO from 'oimo';
let world = new OIMO.World({
timestep: 1/60,
gravity: [0, -9.8, 0],
iterations: 8
});
let ground = world.add({
type: 'box',
size: [100, 1, 100],
pos: [0, -0.5, 0],
move: false
});
let ball = world.add({
type: 'sphere',
size: [1],
pos: [0, 5, 0],
move: true,
density: 1
});
RigidBody создается через метод
world.add(), указывая тип формы, размер, позицию и
подвижность. Статические объекты (move: false) не
участвуют в динамике, но создают коллизии.
Основная функция — world.step(), вызываемая в цикле
отрисовки. Она обновляет позиции и ориентации тел, рассчитывает силы,
столкновения и ограничения суставов.
function update() {
world.step();
let pos = ball.getPosition();
console.log(`Ball position: ${pos}`);
requestAnimationFrame(update);
}
update();
Важно: частота вызова step() должна
соответствовать timestep. Несоответствие приводит к нестабильности и
“прыжкам” объектов.
У объектов Oimo.js есть ключевые свойства:
ball.setLinearVelocity([0, 5, 0]);
ball.setAngularVelocity([0, 0, 1]);
ball.friction = 0.5;
ball.restitution = 0.8;
Oimo.js поддерживает несколько базовых форм:
Каждая форма имеет свои параметры размера и центр масс. Коллизии рассчитываются по геометрии формы с применением алгоритмов bounding volume hierarchy (BVH) для ускорения.
Суставы соединяют два тела и ограничивают их движение:
let hinge = new OIMO.HingeJoint(ball, ground, {
pos1: [0, 5, 0],
pos2: [0, 0, 0],
axis: [0, 0, 1]
});
world.addJoint(hinge);
Параметры суставов позволяют задавать пределы углов, силы пружины и демпфирование.
Для сложных сцен с большим количеством тел эффективным решением является двухпоточная архитектура. Основная идея — разделение:
world.step().Преимущества:
Пример передачи данных в worker:
// main.js
const worker = new Worker('physicsWorker.js');
worker.postMessage({ type: 'init', config: { gravity: [0, -9.8, 0] } });
function update() {
worker.postMessage({ type: 'step' });
requestAnimationFrame(update);
}
// physicsWorker.js
importScripts('oimo.min.js');
let world;
onmess age = function(e) {
const data = e.data;
if(data.type === 'init') {
world = new OIMO.World({ gravity: data.config.gravity });
}
if(data.type === 'step') {
world.step();
postMessage({ positions: world.getPositions() });
}
}
Передача положений и ориентаций обратно в главный поток позволяет синхронизировать визуализацию с физикой.
iterations и timestep
для балансировки точности и производительности.Oimo.js позволяет реализовать сложные физические системы в браузере с высокой производительностью и точностью, особенно при использовании параллельных вычислений через двухпоточную архитектуру. Такой подход обеспечивает плавность и стабильность при симуляции больших сцен с сотнями объектов.