Библиотека Cannon.js предоставляет эффективный физический движок для JavaScript, который позволяет моделировать столкновения, силы и гравитацию. Одной из наиболее гибких форм коллайдеров является Trimesh — трёхмерная сетка, созданная из вершин и треугольников, идеально подходящая для сложных объектов, импортированных из других движков или библиотек, таких как Three.js.
В основе работы с Trimesh лежит структура данных, состоящая из массива вершин и массива индексов треугольников. Для импорта геометрии из Three.js необходимо преобразовать объекты BufferGeometry в формат, понятный Cannon.js.
import * as THREE from 'three';
import * as CANNON from 'cannon-es';
// Пример: загрузка геометрии из Three.js
const loader = new THREE.GLTFLoader();
loader.load('model.gltf', (gltf) => {
const mesh = gltf.scene.children[0]; // берем первый меш
const geometry = mesh.geometry;
// Получение вершин
const vertices = geometry.attributes.position.array;
// Формирование массива индексов треугольников
const indices = geometry.index ? geometry.index.array : undefined;
const cannonTrimesh = createTrimesh(vertices, indices);
});
Чтобы передать геометрию в Cannon.js, необходимо создать
вспомогательную функцию, которая конвертирует
Float32Array вершин и
Uint16Array/Uint32Array индексов в формат, ожидаемый
конструктором CANNON.Trimesh.
function createTrimesh(vertices, indices) {
const verts = [];
const faces = [];
// Преобразование вершин
for (let i = 0; i < vertices.length; i += 3) {
verts.push(vertices[i], vertices[i + 1], vertices[i + 2]);
}
// Преобразование индексов
if (indices) {
for (let i = 0; i < indices.length; i += 3) {
faces.push(indices[i], indices[i + 1], indices[i + 2]);
}
} else {
// Если индексы отсутствуют, предполагается последовательная треугольная сетка
for (let i = 0; i < verts.length / 3; i += 3) {
faces.push(i, i + 1, i + 2);
}
}
return new CANNON.Trimesh(verts, faces);
}
Особенности:
После создания Trimesh его можно использовать для физического тела:
const shape = cannonTrimesh;
const body = new CANNON.Body({
mass: 0, // 0 — статическое тело
shape: shape,
});
body.position.set(0, 0, 0);
world.addBody(body);
Для динамических объектов масса должна быть положительной, но необходимо учитывать, что динамический Trimesh менее оптимален для производительности, чем ConvexPolyhedron. Если объект движется и сталкивается с другими объектами, рекомендуется сначала использовать ConvexHull или разбить модель на несколько упрощённых Trimesh частей.
Three.js и Cannon.js используют одинаковую систему координат, но масштаб объекта должен учитываться при передаче в Trimesh:
mesh.updateMatrixWorld();
const scale = mesh.scale;
const scaledVertices = [];
for (let i = 0; i < vertices.length; i += 3) {
scaledVertices.push(
vertices[i] * scale.x,
vertices[i + 1] * scale.y,
vertices[i + 2] * scale.z
);
}
const cannonTrimesh = createTrimesh(scaledVertices, indices);
Также для корректного позиционирования тел необходимо учитывать локальные преобразования меша, чтобы физическое тело совпадало с визуальным объектом.
Использование Trimesh открывает возможность точно симулировать физику сложных объектов из Three.js, таких как модели автомобилей, зданий или скульптур. Ключевым моментом является корректное преобразование вершин и индексов, учет масштаба и оптимизация для производительности, особенно при динамических телах.
Trimesh предоставляет универсальный инструмент для интеграции визуальных моделей с физическим движком, позволяя добиться реалистичного поведения объектов без необходимости вручную создавать коллизии.