Геометрия объектов в 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] |
Таким образом все ориджины для всех выделенных объектов сместились у нас в плоскость их оснований.