Код для функций doConstraint()
и doTarget()
будет выглядеть так (полный код находится в zoning_constraint.py
):
def doConstraint(obmatrix, targetmatrices, idprop):
obloc = obmatrix.translationPart().resize3D()
obrot = obmatrix.toEuler()
obsca = obmatrix.scalePart()
# Получаем целевой меш
to = Blender.Object.Get(idprop['target_object'])
me = to.getData(mesh=1)
# получаем местоположение целевого объекта
tloc = targetmatrices[0].translationPart().resize3D()
# ищем ближайшую вершину на целевом объекте
smallest = 1000000.0
delta_ob=tloc-obloc
for v in me.verts:
d = (v.co+delta_ob).length
if d < smallest:
smallest=d
sv=v
obloc = sv.co + tloc
# восстанавливаем матрицу объекта
mtxrot = obrot.toMatrix().resize4x4()
mtxloc = Mathutils.TranslationMatrix(obloc)
mtxsca = Mathutils.Matrix([obsca[0],0,0,0],
[0,obsca[1],0,0],
[0,0,obsca[2],0],
[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):
Читать дальше