Trimesh: меш-коллайдер и его ограничения

В Cannon.js Trimesh представляет собой тип коллайдера, предназначенный для объектов сложной, произвольной геометрии. В отличие от простых форм — Box, Sphere или Cylinder — Trimesh позволяет описывать коллизию по фактической сетке вершин и треугольников, что важно для нестандартных или детализированных моделей.

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

const vertices = [
  0, 0, 0,
  1, 0, 0,
  0, 1, 0,
  0, 0, 1
];

const indices = [
  0, 1, 2,
  0, 1, 3,
  0, 2, 3,
  1, 2, 3
];

const mesh = new CANNON.Trimesh(vertices, indices);
  • vertices — плоский массив координат точек: [x0, y0, z0, x1, y1, z1, ...].
  • indices — массив индексов вершин, формирующих треугольники.

Ограничения Trimesh

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

  1. Статические и динамические объекты Trimesh поддерживает как статические, так и динамические тела. Однако использование динамического Trimesh сильно ограничено: столкновения с ним обрабатываются менее точно, а производительность резко падает при большом количестве треугольников.

  2. Сложность коллизий Каждое столкновение требует проверки всех треугольников. Это означает, что на сценах с множеством сложных Trimesh-объектов нагрузка на CPU возрастает экспоненциально.

  3. Невозможность точного реагирования на быстрые движения Trimesh не поддерживает continuous collision detection (CCD) в полной мере. При высокой скорости движения объекты могут “проходить сквозь” Trimesh, особенно если треугольники мелкие.

  4. Неуниверсальные формы Trimesh хорошо подходит для сложных статических объектов, например, рельефа или зданий, но для физически активных элементов предпочтительнее использовать простые коллайдеры: Box, Sphere, ConvexPolyhedron.

Создание динамического Trimesh

Если необходимо динамическое тело с нестандартной геометрией, Cannon.js рекомендует использовать ConvexPolyhedron вместо Trimesh. Trimesh можно применять динамически только при условии низкой частоты обновления и небольшой детализации.

Пример создания Trimesh для статического тела:

const shape = new CANNON.Trimesh(vertices, indices);
const body = new CANNON.Body({ mass: 0 });
body.addShape(shape);
world.addBody(body);

Здесь mass: 0 делает тело статическим, что повышает стабильность и производительность.

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

  • Trimesh корректно взаимодействует с Box и Sphere, но столкновения с другими Trimesh могут быть неточными и медленными.
  • Для упрощения столкновений можно использовать bounding box или ConvexHull, оборачивающий сложную сетку, если точность на уровне треугольников не критична.

Оптимизация использования Trimesh

  1. Уменьшение числа треугольников — ключ к производительности. Модели следует упрощать через decimation.
  2. Разделение на несколько тел — сложный объект можно разбить на несколько Trimesh или ConvexPolyhedron, чтобы ускорить столкновения.
  3. Использование статических тел там, где это возможно — Trimesh на динамических телах сильно тормозит симуляцию.
  4. Кэширование форм и индексов — создание Trimesh с одинаковыми данными повторно может быть ресурсоемким, поэтому желательно хранить один экземпляр.

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

  • Trimesh подходит для сложных статичных ландшафтов, скал, зданий и нестандартных архитектурных элементов.
  • Для динамических объектов лучше преобразовать сетку в ConvexPolyhedron или использовать составные простые коллайдеры.
  • Всегда учитывать нагрузку на CPU при большом количестве треугольников: даже несколько больших Trimesh могут существенно снизить частоту кадров.

Использование Trimesh требует внимательного планирования структуры сцены и компромисса между точностью коллизий и производительностью, особенно в реальном времени.