Читать интересную книгу Написание скриптов для Blender 2.49 - Michel Anders

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 27 28 29 30 31 32 33 34 35 ... 58

                              [0,0,0,1])

    outputmatrix = mtxsca * mtxrot * mtxloc

    return outputmatrix

def doTarget(target_object, subtarget_bone, target_matrix,

             id_prop_of_constr):

   id_props_of_constr['target_object']=target_object.name

   return target_matrix

Выделенные строки показывают, как мы передаем имя целевого объекта в doConstraint(). В doConstraint() мы сначала извлекаем целевой меш. Это может вызвать исключение, например, если целевой объект не является мешем, но оно будет поймано Блендером самостоятельно. Тогда  ограничение не станет воздействовать, ошибка будет показана в консоли, но Блендер продолжит нормальную работу.

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

Выравнивание вдоль вершинной нормали

Теперь, когда мы смогли привязать объект к ближайшей вершине в целевом меше, мы можем видеть, что что-то пропустили: объект не сориентирован в правильном направлении. Это не всегда является проблемой, например, деревья обычно направлены вверх, но во многих ситуациях было бы неплохо, если бы мы смогли сориентировать  ограничиваемый объект перпендикулярно поверхности. Это делается также для всех практических целей, как ориентация ограничиваемого объекта вдоль вершинной нормали той вершины, к которой мы сделали привязку.

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

Для того, чтобы осуществить эту возможность выравнивания, наш код изменится (zoning_constraint.py уже содержит эти изменения): doConstraint() должно вычислять поворотную часть матрицы преобразования. Мы должны вычислить угол вращения, ось вращения, и затем новую матрицу вращения. Выделенная часть следующего кода показывает, что основные инструменты для этих вычислений уже предусмотрены модулем Mathutils:

vnormal = sv.no

if idprop['NormalAlign'] :

    zunit=Mathutils.Vector(0,0,1)

    a=Mathutils.AngleBetweenVecs(vnormal,zunit)

    rotaxis=zunit.cross(vnormal)

    rotmatrix=Mathutils.RotationMatrix(a,4,"r",rotaxis)

    mtxrot = rotmatrix

else:

    mtxrot = obrot.toMatrix().resize4x4()

В предыдущем коде мы можем видеть, что мы сделали выравнивание зависимым от свойства NormalAlign. Только если оно задано, мы вычисляем необходимое преобразование. Следовательно, нам нужно адаптировать также функцию getSettings(), поскольку пользователю нужен способ выбирать, нужно ему выравнивание или нет:

def getSettings(idprop):

    if not idprop.has_key('NormalAlign'):

       idprop['NormalAlign'] = True

    align = Draw.Create(idprop['NormalAlign'])

    block = []

    block.append("Additional restrictions: ")

    block.append(("Alignment: ",align,

                  "Align along vertex normal"))

    retval = Draw.PupBlock("Zoning Constraint", block)

    if (retval):

        idprop['NormalAlign']= align.val

Как показано, свойство NormalAlign по умолчанию будет установлено в True (Истина). Опция затем будет представлена как простое выпадающее меню с кнопкой-переключателем. Если пользователь щелкает за пределами меню или нажимает клавишу Esc,  PupBlock() вернёт значение None, мы не будем изменять свойство NormalAlign. В противном случае, оно будет установлено в соответствии со значением кнопки-переключателя.

Эффекты показаны на иллюстрациях. Первая показывает небольшую ёлку с ограничением привязки к вершине простой подразделенной плоскости земли. Она привязана в точную позицию вершины, но ось z указывает ровно вверх вдоль глобальной оси z. Скриншот показывает ёлку с ограничением к вершине в скалистом пейзаже.

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

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

Привязка к вершинам в вершинной группе

Что, если мы хотим определить конкретно те вершины, к которым мы можем привязать объект? Это можно достигнуть, определив группу вершин, и, затем, рассматривая только вершины из этой группы в качестве кандидатов, к которым можно привязывать. Код необходимый для этого, увеличится всего на несколько строк, и важная часть doConstraint() будет выглядеть примерно так (выделенный код показывает дополнительные строки, имеющие дело с проверкой на принадлежность к группе вершин):

# получаем целевой меш

to = Blender.Object.Get(idprop['target_object'])

me = to.getData(mesh=1)

# получаем положение целевого меша

tloc = targetmatrices[0].translationPart().resize3D()

# ищем ближайшую вершину в целевом объекте

smallest = 1000000.0

delta_ob=tloc-obloc

try:

    verts = me.getVertsFromGroup(idprop['VertexGroup'])

    for vi in verts:

        d = (me.verts[vi].co+delta_ob).length

        if d < smallest :

            smallest = d

            si = vi

        obloc = me.verts[si].co+tloc

        vnormal = me.verts[si].no

except AttributeError:

    for v in me.verts:

        d = (v.co+delta_ob).length

        if d < smallest:

            smallest=d

            sv=v

    obloc = sv.co + tloc

    vnormal = sv.no

Автор здесь нарушил одно из важнейших правил качественного программирования, которое гласит «Нет дублированию кода!» Текст после try желательно переписать, например, так:

try:

    verts = me.getVertsFromGroup(idprop['VertexGroup'])

except AttributeError:

    verts = range(len(me.verts))

for vi in verts:

    d = (me.verts[vi].co+delta_ob).length

    if d < smallest :

        smallest = d

        si = vi

    obloc = me.verts[si].co+tloc

    vnormal = me.verts[si].no

Примечание занудного и наглого переводчика Striver'а

Конструкция try/except гарантирует, что если свойство VertexGroup ссылается на несуществующую группу вершин, мы получим шанс проверить все вершины. Конечно, нам теперь нужен способ для пользователя, позволяющий выбирать группу вершин, так что функцию getSettings() нужно тоже адаптировать. Мы довольствуемся простым полем ввода строки, где можно набрать имя группы вершин. Нет проверки на существование группы, и если мы не хотим ограничиваться привязкой к группе вершин, тогда мы можем или оставить это поле ввода пустым, или занести имя несуществующей группы. Не слишком изящно, но это работает (дополнительные строки выделены):

def getSettings(idprop):

    if not idprop.has_key('VertexGroup'):

       idprop['VertexGroup'] = 'Zone'

    if not idprop.has_key('NormalAlign'):

       idprop['NormalAlign'] = True

    vgroup = Draw.Create(idprop['VertexGroup'])

    align = Draw.Create(idprop['NormalAlign'])

    block = []

    block.append("Additional restrictions: ")

    block.append(("Vertex Group: ",vgroup,0,30,"Vertex

                   Group to restrict location to"))

    block.append(("Alignment: ",align,

                  "Align along vertex normal"))

    retval = Draw.PupBlock("Zoning Constraint", block)

1 ... 27 28 29 30 31 32 33 34 35 ... 58
На этом сайте Вы можете читать книги онлайн бесплатно русская версия Написание скриптов для Blender 2.49 - Michel Anders.

Оставить комментарий