Выбрать главу

def sort_by_parent(pbones):

   bones=[]

   if len(pbones)<1 : return bones

   bone = pbones.pop(0)

   while(not bone.name in bones):

      bones.append(bone.name)

Затем, мы получаем родителя кости, которую мы только что добавили к нашему списку, и настолько долго, насколько мы можем просматривать цепь родителей, мы включаем такого родителя (или, точнее, его имя) в наш список перед текущим элементом (выделено ниже). Если цепь не может следовать дальше, мы выталкиваем новую кость Позы. Когда больше нет костей, метод pop() вызовет исключение IndexError, и мы выходим из нашего цикла while:

      parent = bone.parent

      while(parent):

         if not parent.name in bones:

            bones.insert(bones.index(bone.name),

                         parent.name)

         bone = parent

         parent = parent.parent

      try:

         bone = pbones.pop(0)

      except IndexError:

         break

   return bones

Чем дольше я пытался разобраться с логикой этой функции, чтобы адекватно перевести два предыдущих абзаца, тем сильнее мне это не нравилось, ибо логики я не наблюдал. Тогда я немного потестировал эту функцию в файле peristaltic.blend, и убедился, что она правильно работает не во всех случаях. Цепочка костей в файле по направлению от родительских к дочерним выглядит так: ['Bone', 'Bone.001', 'Bone.002', 'Bone.003', 'Bone.004', 'Bone.005']. Если на вход функции список pbones приходит в таком порядке: ["Bone.001", "Bone.002", "Bone.003", "Bone.004", "Bone.005", "Bone"], то результат получается таким, каким надо, но если на вход придёт, например, список ["Bone.002", "Bone.001", "Bone.003", "Bone.004", "Bone.005", "Bone"] (первые два элемента поменяны местами), то на выходе будет всего 3 кости: ['Bone', 'Bone.001', 'Bone.002']. Вот мой исправленный вариант функции:

def sort_by_parent(pbones):

    bones=[]

    while True:  # Бесконечный цикл гарантирует перебор

                 # всех костей из входного списка

        try:

           bone = pbones.pop(0)

        except IndexError:

           break # Единственное условие выхода из цикла

        if not bone.name in bones:

            bones.append(bone.name)

            parent = bone.parent

            while(parent):

               if not parent.name in bones:

                  bones.insert(bones.index(bone.name),

                               parent.name)

               bone = parent

               parent = parent.parent

    return bones

- Добавление переводчика.

Следующий шаг - это определение самого скрипта. Сначала, мы получаем активный объект в текущей сцене и проверяем, что это - на самом деле арматура. Если нет, мы предупреждаем об этом пользователя с помощью всплывающего сообщения (выделенная часть следующего кода), в противном случае мы продолжаем и получаем связанные с арматурой данные методом getData():

scn = Blender.Scene.GetCurrent()

arm = scn.objects.active

if arm.getType()!='Armature':

   Blender.Draw.PupMenu("Selected object is not an " +

                        "Armature%t|Ok")

else:

   adata = arm.getData()

Затем, мы делаем арматуру редактируемой и убеждаемся, что у каждой кости задана опция HINGE (выделено). Преобразование списка опций в множество (set) и обратно в список после добавления опций HINGE является способом удостовериться, что эта опция появится в списке только один раз.

   adata.makeEditable()

   for ebone in adata.bones.values():

      ebone.options =

        list(set(ebone.options)|

             set([Blender.Armature.HINGE]))

   adata.update()

Поза связана с объектом арматуры, а не со своими данными, так что мы получаем её из объекта arm, используя метод getPose(). Позы кости очень похожи на обычные IPO, но они должны быть связаны с действием (action), которое группирует эти позы. При работе с Блендером интерактивно действие создаётся автоматически, как только мы вставим ключевой кадр в позу, но в скрипте мы должны явно создать действие, если оно ещё не присутствует (выделено):