Скрипт, который мы написали для быстрого перемещения ориджина объекта к выделению, работает хорошо. До тех пор, пока в сцене не используются инстансы. Стоит нам переместить ориджин к выделению на объекте, у которого есть инстансы, мы увидим, что все инстансы объекта переместились со своих мест в новое положение. Так происходит потому, что перемещая ориджин относительно геометрии объекта мы, по сути, перемещаем саму геометрию относительно ее центральной точки, а потом уже эту центральную точку – ориджин, ставим в новое положение. Для инстансов же ориджин остается на тот же месте, где и был, а смещение геометрии никуда не исчезает, и поэтому получается скачок.
Исправить ситуацию достаточно просто. По сути, нам нужно просто пройтись по всем инстансам объекта и сместить их ориджины так, чтобы смещенная геометрия вернулась на место.
Доработаем наш скрипт так, чтобы учитывать поведение инстансов и возвращать их на место при переносе ориждина к центру выделения.
Начинаем мы, как и раньше с фиксации положения курсора.
|
1 |
cursor_src_location = bpy.context.scene.cursor.location.copy() |
А теперь, для будущей коррекции, соберем список инстансов текущего объекта. Сам активный объект в него включать не будем, так как мы работаем изначально с ним и он не требует дополнительной коррекции положения.
Сохраним в списке инстансов указатель на собственно инстанс, а так же на его мировую матрицу, она нам понадобится для дальнейших преобразований.
|
1 2 3 4 5 |
instances = [(o, o.matrix_world) for o in bpy.data.objects \ if o.data == bpy.context.object.data and o != bpy.context.active_object] # [(bpy.data.objects['Cube.001'], Matrix(((-0.6280437707901001, -0.21648520231246948, -0.7474591135978699, 2.7219083309173584), ... # (bpy.data.objects['Cube.003'], Matrix(((0.4437439739704132, -0.6796227693557739, -0.5841268301010132, 4.871679782867432), ...] |
Как и раньше, получим положение текущего объекта, и указатель на рабочую область 3D вьюпорта для использования переопределения контекста (чтобы скрипт правильно отработал при вызове из Text Edit)
|
1 2 3 4 |
obj_location = bpy.context.object.location area = next((area for area in bpy.context.screen.areas if area.type == 'VIEW_3D')) |
Переопределим контекст и поместим курсор в центр текущего выделения.
|
1 2 |
with bpy.context.temp_override(area=area): bpy.ops.view3d.snap_cursor_to_selected() |
А теперь вернемся к дополнительным преобразованиям, нужным для починки инстансов.
Чтобы вернуть инстансы по своим местам, нам нужно понять, насколько геометрия сместилась относительно ориджина. Кажется это просто, нужно взять вектор из текущего центра объекта в положение курсора, куда он будет перемещен.
Однако еще обязательно нужно учесть, что на текущий объект могут влить не примененные трансформации, поэтому вектор в позицию курсора нам нужно умножить на инвертированную матрицу трансформаций объекта.
|
1 2 3 |
cur_lok = bpy.context.object.matrix_world.inverted() @ bpy.context.scene.cursor.location # <Vector (-1.0000, 1.0000, 1.0000)> |
Вот именно на этот вектор нам и нужно будет сдвинуть все инстансы.
Теперь переместим ориджин текущего объекта к центру выделения, как мы делали это раньше.
|
1 2 |
bpy.ops.object.mode_set(mode = 'OBJECT') bpy.ops.object.origin_set(type='ORIGIN_CURSOR') |
А перед возвратом в режим редактирования, сделаем корректирующие смещения для всех полученных ранее инстансов.
|
1 2 |
for _o, _m in instances: _o.location = (_m @ cur_lok) |
Так как у инстансов могут быть свои не примененные матрицы трансформации, мы должны это учесть и поэтому локальное смещение ориджина для геометрии мы умножаем на матрицу трансформации инстанса.
Нам осталось вернуться в режим редактирования и переместить курсор в исходное положение в сцене.
|
1 2 3 |
bpy.ops.object.mode_set(mode = 'EDIT') bpy.context.scene.cursor.location = cursor_src_location |
Если мы выполним наш скрипт теперь, с учетом доработок, все инстансы объекта останутся на своих исходных местах.

.blend file on Patreon