Изучаем петли (loops)

В общем понимании «петля» или в терминологии 3D – «луп» (с английского loop – петля) обычно представляет собой последовательное выделение нескольких точек, ребер или полигонов меша.

Однако во внутренней структуре меша присутствует отдельный элемент, который тоже называется «луп» (будем придерживаться терминологии) и представляет собой условную комбинацию одного вертекса с одним ребром меша. Попробуем разобраться, для чего нужны эти «лупы».

Рассмотрим отдельный полигон, имеющий 4 вертекса и 4 ребра.

Полигон имеет 4 лупа:

  1. Вертекс 0 + ребро 1
  2. Вертекс 1 + ребро 2
  3. Вертекс 3 + ребро 3
  4. Вертекс 2 + ребро 0

Лупы не сортируются в соответствии с индексами вертексов или ребер. Определить, какой из лупов считается условно первым можно с помощью свойства полигона loop_start, возвращающего индекс ребра, которое берется за точку отсчета:

Луп всегда направлен против часовой стрелки в правосторонней системе координат (нормаль полигона направлена вверх). Луп всегда выходит из вертекса и идет вдоль ребра.

В нашем примере первый луп выходит из вертекса с индексом 2 и идет вдоль ребра с индексом 0.

Общее количество лупов полигона можно получить с помощью свойства полигона loop_total:

А список индексов лупов на полигоне — через range свойство loop_indices:

В самом меше хранится список всех лупов этого меша:

Если луп полигона – это просто вертекс плюс ребро, луп в структуре меша имеет собственный тип:

Через MeshLoop можно получить индексы принадлежащих этому лупу вертекса и ребра:

Кажется, что лупы в структуре меша не имеют особой ценности, однако это не так. Например, для получения соответствия полигонов меша с полигонами его развертки (UV) используются механизмы лупов.

С помощью лупа на полигоне можно получить соответствующий ему луп на UV-развертке меша:

А через луп полигона развертки — получить координаты его вертекса:

Таким образом вертекс меша соотносится с его положением на развертке, что дает возможность манипулировать наложением текстуры.

Механизм лупов реализован так же и во внутреннем формате меша Blender – BMesh.

Механизм лупов в BMesh реализован гораздо шире. Луп имеет свой тип:

и большой набор свойств и методов.

Однако принцип реализации остается неизменным: луп представляет собой комбинацию вертекс + ребро.

Для получения лупов, принадлежащих полигону, нужно выполнить следующий код:

Для каждого лупа очень просто получить полигон, которому принадлежит луп, ребро, вдоль которого он идет, и вертекс, из которого луп выходит.

Второй вертекс (в который приходит луп), можно получить с помощью метода other_vert ребра BMEdge:

Для ребер и вертексов BMesh так же есть возможность получить лупы, проходящие через них при помощи свойства link_loops.

Метод

выполняется для каждого набора полигонов, ребер и вертексов BMesh для того, чтобы упорядочить таблицы индексов этих элементов.

Кроме упрощенного доступа к лупам, в BMesh предусмотрен очень удобный механизм перехода между соседними лупами.

Возьмем для примера любой луп полигона. Вызов свойства

всегда вернет предыдущий по порядку луп полигона.

Свойство

по аналогии всегда возвращает следующий по порядку луп полигона.

А так как у полученный луп имеет те же самые свойства link_loop_prev и link_loop_next, можно например получить луп, лежащий на противоположной стороне полигона:

или

Еще одно удобное свойство для перехода между лупами

и

позволяет перейти от текущего лупа к лупу, лежащему на том же ребре но на соседнем полигоне и направленному в противоположную сторону.

В большинстве случаев для обычной геометрии мешей link_loop_radial_next и link_loop_radial_prev указывают на один и тот же луп.

Они будут указывать на разные лупы только если в одно общее ребро сходятся больше двух полигонов. В этом случае каждый следующий вызов link_loop_radial_next укажет на луп, лежащий на следующем по порядку полигоне ребра, а link_loop_radial_prev — на предыдущий.

Наглядной демонстрацией перехода между лупами может служить пример выделения нескольких ребер друг за другом (подобное выделение производится кликом с зажатой клавишей alt). Возьмем за начало отсчета одно выделенное ребро на меше. Добавим к выделению еще 3 ребра, лежащие по направлению следования исходного.

Для того, чтобы перейти от текущего ребра к последующему, нужно выполнить цепочку переходов между лупами:

и выделить ребро, принадлежащее полученному лупу.

Повторяя алгоритм перехода между лупами и выделяя полученные ребра необходимое число раз, получим требуемое выделение.

Полный код скрипта, выделяющего 3 следующих ребра, начиная с выделенного:

При всем удобстве механизма лупов, не всегда лупы ведут себя предсказуемо.

Например на срезе меша, если полигон не имеет соседнего полигона, свойства link_loop_radial_next (link_loop_radial_prev) и link_loop_next (link_loop_prev) укажут на один и тот же луп.

Поэтому команда

в случае попадания на срез меша укажет на луп, принадлежащий противоположному ребру того же полигона, а не на перпендикулярное ребро соседнего полигона, как может быть ожидалось.

В тоже время, если срез заполнен энгоном, та же самая комбинация переходов выведет именно на ожидаемое перпендикулярное ребро соседнего полигона.