Инициализация Oimo.js внутри Worker

Использование Oimo.js в контексте Web Worker позволяет перенести все вычисления физического движка в отдельный поток, освобождая основной поток браузера от интенсивных расчётов и обеспечивая плавность визуализации. Этот подход особенно эффективен для сложных симуляций с большим количеством тел.

Подключение библиотеки в Worker

Oimo.js может быть использован как модуль через importScripts или в виде ES-модуля. Для классического Worker:

// worker.js
importScripts('path/to/oimo.min.js');

// Создание мира
const world = new OIMO.World({
    timestep: 1/60,
    iterations: 8,
    broadphase: 2, // 1: brute force, 2: sweep & prune, 3: volume tree
    worldscale: 1,
    random: true,
    info: false
});

Для использования ES-модулей (type="module"):

// worker-module.js
import * as OIMO from './oimo.module.js';

const world = new OIMO.World({
    timestep: 1/60,
    iterations: 8,
    broadphase: 2,
    worldscale: 1,
    random: true,
    info: false
});

Создание и добавление тел в мир

Oimo.js работает с объектами RigidBody, которые обладают формами (Shape) и физическими свойствами. В Worker инициализация тел выглядит следующим образом:

// Создание тела куба
const box = world.add({
    type: "box",
    size: [1, 1, 1],
    pos: [0, 5, 0],
    rot: [0, 0, 0],
    move: true,
    density: 1,
    friction: 0.5,
    restitution: 0.3
});

// Создание статической плоскости
world.add({
    type: "box",
    size: [10, 1, 10],
    pos: [0, 0, 0],
    move: false
});

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

  • type: "box", "sphere", "cylinder".
  • size: массив размеров [width, height, depth] для коробок.
  • pos: координаты [x, y, z].
  • rot: поворот в радианах [x, y, z].
  • move: подвижность тела (true для динамического, false для статического).
  • density, friction, restitution: физические свойства материала.

Основной цикл симуляции в Worker

Работа физического мира требует регулярного обновления состояния тел. В Worker это организуется через setInterval или requestAnimationFrame с использованием postMessage для передачи состояния обратно в основной поток:

function upd ate() {
    world.step();

    const bodiesState = world.rigidBodies.map(body => ({
        id: body.id,
        position: body.getPosition(),
        rotation: body.getQuaternion()
    }));

    postMessage({ type: 'upd ate', bodies: bodiesState });
}

setInterval(update, 1000/60); // Обновление 60 раз в секунду

Передача данных между Worker и основным потоком

Worker не имеет доступа к DOM, поэтому все визуальные данные должны передаваться через postMessage. В основном потоке:

const worker = new Worker('worker.js');

worker.onmess age = (event) => {
    const data = event.data;
    if (data.type === 'update') {
        data.bodies.forEach(body => {
            const mesh = scene.getObjectById(body.id);
            mesh.position.se t(...body.position);
            mesh.quaternion.se t(...body.rotation);
        });
    }
};

Использование структурированных клонов или Transferable Objects позволяет снизить нагрузку при передаче больших массивов данных, особенно при работе с сотнями тел.

Инициализация связей и коллизий

Oimo.js поддерживает Constraints (соединения) между телами. В Worker добавление соединений выполняется аналогично добавлению тел:

const joint = world.add({
    type: 'jointDistance',
    body1: box1,
    body2: box2,
    minDistance: 1,
    maxDistance: 2,
    collideConnected: false
});

Основные типы соединений:

  • jointDistance — поддерживает фиксированное расстояние.
  • jointHinge — реализует шарнирное вращение.
  • jointSlider — ограничивает движение вдоль оси.

Оптимизация работы в Worker

  • Использовать Broadphase 2 или 3 для крупных сцен.
  • Ограничивать частоту postMessage — передавать состояние не чаще 60 Гц.
  • При большом количестве тел передавать только изменившиеся позиции или использовать Float32Array для буферов.
  • Включать random: true для лучшего распределения столкновений при динамических телах.

Особенности работы Oimo.js в многопоточном окружении

  • Worker изолирован от DOM, поэтому все визуальные элементы создаются и обновляются в основном потоке.
  • Коллизии и физические расчёты полностью выполняются в отдельном потоке, что предотвращает “подвисания” интерфейса.
  • Все события столкновений и триггеров должны передаваться через сообщения.

Пример полной инициализации

importScripts('oimo.min.js');

const world = new OIMO.World({ timestep: 1/60, iterations: 8, broadphase: 2 });

const ground = world.add({ type: "box", size: [20, 1, 20], pos: [0, 0, 0], move: false });
const cube = world.add({ type: "box", size: [1, 1, 1], pos: [0, 5, 0], move: true, density: 1 });

setInterval(() => {
    world.step();
    postMessage({
        bodies: world.rigidBodies.map(b => ({
            id: b.id,
            pos: b.getPosition(),
            rot: b.getQuaternion()
        }))
    });
}, 1000/60);

Этот подход обеспечивает полное разделение вычислений и визуализации, делает физический движок Oimo.js максимально производительным в браузере, а Worker — эффективным инструментом для масштабируемых симуляций.