При работе с объектом bmesh в Blender иногда возникает необходимость взять часть геометрии и перенести ее в отдельный меш. Рассмотрим, как это можно сделать при помощи Blender Python API.
Возьмем для примера объект, в котором у нас есть несколько выделенных полигонов. Создадим объект bmesh и перенесем в него данные с нашего текущего объекта.
Приведем в соответствие индексы точек и полигонов bmesh к индексам исходного меша.
Работать нужно в объектном режиме.
|
1 2 3 4 5 6 |
bpy.ops.object.mode_set(mode='OBJECT') bm = bmesh.new() bm.from_mesh(bpy.context.active_object.data) bm.verts.ensure_lookup_table() bm.faces.ensure_lookup_table() |
Пусть теперь нам нужно отделить выделенные полигоны в другой объект.
Список выделенных полигонов:
|
1 2 3 |
selected_faces = [face for face in bm.faces if face.select] # [<BMFace(0x0000019D76900E50), index=0, totverts=4>, ...] |
Получим список вертексов для всех выделенных полигонов.
|
1 2 3 |
vertices = list(set(_vert for _face in selected_faces for _vert in _face.verts)) # [<BMVert(0x0000019D768B0000), index=26>, <BMVert(0x0000019D768B0038), index=27>, ...] |
И далее, список их координат.
|
1 2 3 |
coordinates = [v.co for v in vertices] # [Vector((0.4609375, -0.703125, 0.1171875)), Vector((0.625, -0.6484375, 0.1875)), ...] |
Теперь нам нужно получить список через который мы сможем сопоставить индексы вертексов для полигонов из списка selected_faces с порядковым расположением вертексов в списке vertices.
Создадим словарь, в котором ключами будут являться индексы точек, а значениями – из порядковое положение в списке vertices.
|
1 2 3 4 5 |
vmap = dict() for i, vert in enumerate(vertices): vmap[vert.index] = i # {231: 0, 232: 1, 233: 2, ...} |
И с его помощью свяжем каждый полигон с порядковым расположением индексов его вертексов.
|
1 2 3 4 5 |
faces = [] for face in selected_faces: faces.append([vmap[_vert.index] for _vert in face.verts]) # [[169, 109, 113, 167], [115, 111, 170, 168], ...] |
Получив все нужные данные для переноса выделенной геометрии в новый меш, мы можем удалить ее в текущем объекте.
|
1 2 3 4 |
bmesh.ops.delete(bm, geom=selected_faces, context='FACES') bm.to_mesh(bpy.context.active_object.data) bm.free() |
Теперь создадим новый меш на основе данных, полученных из исходного меша.
|
1 2 |
me = bpy.data.meshes.new(name='new_mesh') me.from_pydata(coordinates, [], faces) |
И прилинкуем его в сцену в ту же коллекцию.
|
1 2 |
obj = bpy.data.objects.new('new_object', me) bpy.data.collections['Collection'].objects.link(obj) |
После этого новый объект появится в 3D вьюпорте.
Обратите внимание, что на самом деле мы не “копировали” геометрию, а взяли данные с исходного меша и создали совершенно новый меш по этим данным. Поэтому в этом случае у нового меша будут отсутствовать материалы, текстуры, развертки и т.п., которые могли быть на исходном меше. Так же мы работали в локальных координатах, и если у исходного меша были непримененные трансформации, то координаты точек нового меша дополнительно нужно умножить на матрицу мира исходного меша.

.blend file on Patreon