Одной из частых задач при построении сцены в Blender является контроль пересечения мешей. Определить пересекаются ли два меша между собой можно, используя технологию ограничивающих объемов (Bounding Volume).
Модуль “mathutils” предоставляет нам возможность легко и просто построить деревья иерархии ограничивающих объемов Bounding Volume Hierarchy Tree (BVH-Tree) для любых мешей в сцене, и с их помощью проводить проверку на пересечения.
Для примера добавим в сцену UV-Сферу (shift + a – Mesh – UV Sphere) и плоскость (shift + a – Mesh – Plane). Для плоскости добавим подразделений (Subdivide) в режиме редактирования.
Для того, чтобы BVH-деревья можно было корректно проверить на пересечения, меши, по которым они строятся, должны находиться в одной системе координат.
Вычислим точки обоих мешей (плоскости и сферы) в единой системе координат – мировой. Для этого умножим координаты каждой точки меша на его матрицу мира.
1 2 3 4 5 6 7 8 9 |
mesh_1 = bpy.data.objects['Plane'] m_1 = mesh_1.matrix_world.copy() mesh_1_verts = [m_1 @ vertex.co for vertex in mesh_1.data.vertices] mesh_1_polys = [polygon.vertices for polygon in mesh_1.data.polygons] mesh_2 = bpy.data.objects['Sphere'] m_2 = mesh_2.matrix_world.copy() mesh_2_verts = [m_2 @ vertex.co for vertex in mesh_2.data.vertices] mesh_2_polys = [polygon.vertices for polygon in mesh_2.data.polygons] |
Также мы получили списки полигонов меша с относящимися к ним вертексами.
Имея эти два списка (координат точек и соотношений точек с полигонами), мы можем построить для каждого меша дерево ограничивающих объемов BVH-Tree:
1 2 3 4 |
from mathutils.bvhtree import BVHTree mesh_1_bvh_tree = BVHTree.FromPolygons(mesh_1_verts, mesh_1_polys) mesh_2_bvh_tree = BVHTree.FromPolygons(mesh_2_verts, mesh_2_polys) |
Для двух BVH-деревьев можно легко найти пересечения:
1 2 3 |
intersections = mesh_1_bvh_tree.overlap(mesh_2_bvh_tree) # [(4, 133), (1011, 133), (1011, 156), (243, 156), ... ] |
Метод “overlap” возвращает список индексов пересекающихся полигонов в виде пар:
(“индекс полигона меша 1”, “индекс полигона меша 2”).
Для простого понимания, пересекаются ли проверяемые меши или нет, достаточно проверить, что в полученном списке есть хоть одно значение.
1 2 3 4 5 6 7 |
if intersections: print('MESHES INTERSECTS') print(intersections) else: print('NO INTERSECTIONS') # MESHES INTERSECTS |
Для более детальной проверки пересечений, разберем полученный список на два отдельных списка, каждый из которых относится к своему мешу:
1 2 3 4 5 6 7 |
mesh_1_polys_ints = [pair[0] for pair in intersections] # [4, 1011, 1011, 243, ...] mesh_2_polys_ints = [pair[1] for pair in intersections] # [133, 133, 156, 156, ...] |
Теперь мы можем выделить на каждом меше полигоны, имеющие пересечения (в объектном режиме).
1 2 3 4 5 6 7 |
for face in mesh_1.data.polygons: if face.index in mesh_1_polys_ints: face.select = True for face in mesh_2.data.polygons: if face.index in mesh_2_polys_ints: face.select = True |