Книга: Написание скриптов для Blender 2.49

Get a bit of backbone boy!

Get a bit of backbone boy!

Всё, что мы узнали уже об остнастке, может быть применено также к  creepycrawlies.py. Если мы хотим расширить функциональность сгенерированной модели, мы можем соединить модификатор арматуры со сгенерированным мешем. Мы также создадим объект арматуры с подходящим набором костей.

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

Давайте посмотрим на то, как могли бы быть осуществлены некоторые существенные элементы (полный код смотрите в creepycrawlies.py). Сначала мы должны создать арматуру и сделать её редактируемой для добавления костей:

   ar = Blender.Armature.New('BugBones')
   ar.autoIK = True
   obbones = scn.objects.new(ar)
   ar.makeEditable()

Мы можем также задать любые атрибуты, которые изменяют поведение арматуры или способ её отображения. Здесь мы просто включаем свойство autoIK, так как это сделает манипуляции хвостом нашего создания, возможно очень длинным, намного проще для аниматора.

Следующий шаг - это создание костей для каждого набора вершин. Список vgroup в следующем коде содержит кортежи (vg,vlist,parent,connected), где vg - имя группы вершин а vlist - список индексов вершин, принадлежащих этой группе. Каждая кость, которую мы создаем, может иметь родителя и может физически быть соединена с родителем. Эти условия задаются частями кортежа parent (родитель) и connected (соединён):

   for vg,vlist,parent,connected in vgroup:
      bone = Blender.Armature.Editbone()
      bb = bounding_box([verts[i] for i in vlist])

Для каждой кости, которую мы создаем, мы вычисляем габаритный ящик (bounding box) всех вершин в группе, на которые эта кость будет влиять. Дальше мы должны разместить кость. При способе, которым мы настраивали наше создание, все сегменты его тела вытягивались вдоль оси y, за исключением крыльев (wing) и ног (leg). Они вытягивались вдоль оси x. Мы сначала проверяем этот факт, и соответственно устанавливаем переменную axis (ось):

      axis=1
      if vg.startswith('wing') or vg.startswith('leg'):
         axis = 0

Кости в составе арматуры индексируются по имени и позиции концов костей, сохраненных в их атрибутах head (голова) и tail (конец) соответственно. Так, если у нас есть родительская кость, и мы хотим определить её среднее значение координаты y, мы можем вычислить это следующим способом:

      if parent != None :
         parenty = (ar.bones[parent].head[1] +
                    ar.bones[parent].tail[1])/2.0

Мы вычисляем эту позицию, поскольку такие части как, например, ноги и крылья имеют родительские кости (то есть, они перемещаются вместе с родительской костью), но не подсоединены головой к хвосту. Мы разместим эти кости, начиная в центре родительской кости, и для этого нам нужна позиция родителя по y. Кости сегментов, лежащих вдоль оси y, сами позиционируются вдоль оси y, и, таким образом, имеют нулевые координаты x и z. Координаты x и z ног и сегментов крыльев берутся из их габаритных ящиков. Если кость подсоединена (connected), мы просто устанавливаем позицию её головы в копию позиции хвоста родителя (выделено ниже).


Класс Блендера Vector предоставляет функцию copy(), но как ни странно, нет функции __copy__(), так что он не будет играть по правилам с функциями из модуля Питона copy.

      if connected:
         bone.head = ar.bones[parent].tail.copy()
      else:
         if axis==1:
            bone.head=Blender.Mathutils.Vector(0,
                             bb[1][0],0)
         else:
            bone.head=Blender.Mathutils.Vector(bb[0][1],
                             parenty,bb[2][1])

Положение хвоста кости рассчитывается аналогичным образом:

      if axis==1:
         bone.tail=Blender.Mathutils.Vector(0,bb[1][1],0)
      else:
         bone.tail=Blender.Mathutils.Vector(bb[0][0],
                                       parenty, bb[2][0])

Последние шаги в создании кости - это добавление её к арматуре и установка специфичных для костей опций и всех родительских связей.

      ar.bones[vg] = bone
      if parent != None :
         bone.parent=ar.bones[parent]
      else:
         bone.clearParent()
      if connected:
         bone.options=Blender.Armature.CONNECTED

Заметьте, что в предыдущем коде важен порядок действий: атрибут parent может быть присвоен или очищен только у костей, которые добавлены к арматуре, а опция CONNECTED может быть установлена только у кости, имеющей родителя.

Кроме того, мы должны остерегаться здесь некоторой специфичности Блендера. Родитель может быть установлен у кости назначением в её атрибут parent. Если у него нет родителя, этот атрибут возвращает None. Тем не менее, мы не можем назначить None в этот атрибут, мы должны использовать функцию clearParent(), чтобы удалить родительские отношения.


Оглавление книги

Оглавление статьи/книги

Генерация: 1.295. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз