Геометрия объектов в Blender всегда строится вокруг ориджина (origin) – точки, которая в локальной системе объекта берется за начальную. И если ориджин не лежит в плоскости основания объекта, бывает сложно разместить этот объект на поверхности другого объекта. Быстро поставить ориджин в нижнюю точку для всех выделенных объектов сцены мы можем при помощи скрипта на Blender Python API.
Чтобы “сбросить” ориджин в нижнюю точку для каждого из выделенных объектов, мы можем сдвинуть сам объект вниз на высоту его ориджина, а все его вертексы поднять на ту же высоту вверх. Таким образом местоположение геометрии объекта не изменится, а его ориджин будет сдвинут к его основанию.
Пройдем в цикле по всем выделенным объектам:
|
1 |
for obj in bpy.context.selected_objects: |
Сначала изменим координаты всех вертексов объекта, подняв их вверх.
Чтобы понять, насколько именно нам нужно сдвинуть вертексы, мы должны найти самый нижний вертекс, лежащий в основании объекта, и из текущей координаты ориджина по оси Z вычесть координату этого вертекса по той же оси.
Текущая координата ориджина по оси Z:
|
1 |
origin_z = obj.location.z |
Так как трансформации, например поворот, к объекту могут быть еще не применены, нам нужно получить глобальные значения координат для вертексов.
Для этого умножим координату вертекса на мировую матрицу объекта.
|
1 2 3 |
vertices_world = [obj.matrix_world @ vertex.co for vertex in obj.data.vertices] # [Vector((-3.0, 1.0, 0.004944801330566406)), ... ] |
Теперь мы можем найти вертекс с наименьшей по оси Z координатой.
|
1 2 3 |
min_vert_co = min(vertices_world, key = lambda co: co.z) # <Vector (-3.0000, 1.0000, 0.0049)> |
И получить вектор для смещения вертексов.
|
1 2 3 |
move = (0.0, 0.0, obj.location.z - min_vert_co.z) # (0.0, 0.0, 4.733994960784912) |
Так как трансформации к объекту могут быть не применены, мы не можем просто сдвинуть вертексы на полученный вектор. Нам нужно учесть влияние матрицы трансформаций.
Поэтому сначала сформируем матрицу для перемещения вертексов из полученного вектора.
|
1 2 3 4 5 6 |
translate = Matrix.Translation(move) # <Matrix 4x4 (1.0000, 0.0000, 0.0000, 0.0000) # (0.0000, 1.0000, 0.0000, 0.0000) # (0.0000, 0.0000, 1.0000, 4.7340) # (0.0000, 0.0000, 0.0000, 1.0000)> |
Сохраним копию мировой матрицы трансформации объекта, чтобы исключить промежуточное влияние. А так же, ее инвертированную версию.
|
1 2 |
m_world = obj.matrix_world.copy() m_world_i = m_world.inverted() |
Теперь наконец мы можем начать перемещать вертексы.
Для каждого вертекса нашего объекта: сначала сбросим влияние матрицы трансформации, потом применим наше смещение по оси Z, после чего вернем влияние мировых трансформаций обратно.
|
1 2 3 4 |
for vertex in obj.data.vertices: vertex.co = m_world @ vertex.co vertex.co = translate @ vertex.co vertex.co = m_world_i @ vertex.co |
Сейчас мы можем видеть, как все вертексы объекта сместились вверх по оси Z так, что встали на уровне (или выше) ориджина.
Осталось сместить сам ориджин, т.е. целиком объект, вниз, чтобы вернуть геометрию на исходное место.
|
1 |
obj.location.z -= move[2] |
Таким образом все ориджины для всех выделенных объектов сместились у нас в плоскость их оснований.

.blend file on Patreon
как альтернативный вариант выставить 3д курсор в нужную позицию и применить оператор bpy.ops.object.origin_set(type=’ORIGIN_CURSOR’, center=’MEDIAN’)
и вернуть курсор в изначальное положение
Да, можно и так. Но в Blender, если можно что-то сделать кодом без операторов, лучше это делать без операторов.
данный оператор дает стабильный и быстрый результат, при приемлемых минусах от использования оператора.
Но при изменении прохода по каждому вертексу, и смещения его дают накопления округлений (float3d больших координатах, скейлах). Плюс сам цикл по вершинам на питоне медленный, тем более при большом их количестве. Еще могут возникнуть проблемы при линкованных данных с obj.data. И на сколько помню у меня были отзывы от пользователей по поводу shape keys, их нужно тоже апдейтить.
Как вариант можно использовать смещение данных через data.transform
там кстати есть апдейт shepe.keys’ов
Операторы в Blender контестно зависимые, режимо зависимые, инструменто зависимые и не масштабируемые. Это налагает определенные сложности при их использовании. Поэтому где можно обойтись без операторов, я стараюсь обойтись без них. И в тоже время я не умаляю достоинств операторов. Если в вашем случае удобнее и практичнее использовать оператор – конечно нужно его использовать.