本来心血来潮想写个做锁子甲的脚本,最后弄成了个类似铁链的效果。不过也挺有意思,要是弄铁链的时候还是挺方便的。
研究的过程还是挺有意思的。在一开始的时候想的很简单。调用一个方法直接把要复制的对象Y轴对齐到平面的法线方向上,但是无奈怎么也没找到Maya里面有这个方法。纠结了一段时间以后只能把旋转分为两次。在物体的局部的X,Z轴上分别旋转两次把Y轴对齐到法线上,这样就可以吧物体附着到面上了。
沿着边进行复制的时候就简单多了。首先取相邻面的发线求平均值,再用任意一个面的中心位置减去另一个中心位置,标准化后求出方向,然后这两个向量叉乘。那么用这三个向量构建出的空间变换矩阵就是沿着边复制几何体的矩阵了。
附结果图,蓝色圆环是复制到几何体上的。单独的蓝色圆环下面的面积是用来进行参考这样就可以根据曲面大小自动缩放,需要注意的是越是正方形的链接效果越好。
# -*- coding:utf-8 -*-
import pymel.core as pm
import math
#运行报错的请删除中文注释
dup = pm.ls(selection=True)#选择要复制的物体运行这一行。
faceArea = pm.ls(selection=True, flatten=True)[0].getArea(space='world')#选择参考面,注意是选择面,不是物体。运行这一行
obj = pm.listRelatives(pm.ls(selection=True), shapes=True)#选择将要附着的对象,运行这一行
def faceCenter(faces):
nub = 0
averX = 0.0
averY = 0.0
averZ = 0.0
for i in faces.getPoints(space='world'):
nub += 1
averX += i.x
averY += i.y
averZ += i.z
return averX / nub, averY / nub, averZ / nub
def edgeCenter(edge):
averX = (edge.getPoint(0, space='world').x + edge.getPoint(1, space='world').x) / 2
averY = (edge.getPoint(0, space='world').y + edge.getPoint(1, space='world').y) / 2
averZ = (edge.getPoint(0, space='world').z + edge.getPoint(1, space='world').z) / 2
return averX, averY, averZ
def RotateDupObj(face, rotatObj):
faceN = face
Rx = math.atan2(faceN.z, faceN.y) * (180 / math.pi) # Xaxis
Pt = faceN.z / (
math.sin(math.atan2(faceN.z, faceN.y)) if abs(math.sin(math.atan2(faceN.z, faceN.y))) > 0.00001 else 0.00001)
Rz = math.atan2(faceN.x, Pt) * (180 / math.pi) * (-1)
pm.rotate(rotatObj, Rx, x=True, objectSpace=True, relative=True)#沿着物体的局部坐标进行两次旋转使物体的Y轴对齐法线方向
pm.rotate(rotatObj, Rz, z=True, objectSpace=True, relative=True)#沿着物体的局部坐标进行两次旋转使物体的Y轴对齐法线方向
pm.rotate(rotatObj, -rotatObj[0].getRotation(space='object').y, y=True, objectSpace=True, relative=True)#想将Y轴旋转归零,经测试运行多次这一行代码才有效,这一行删了也不影响。
#沿面进行复制
for f in obj[0].faces:
faceC = faceCenter(f)
faceScale = math.sqrt(f.getArea(space='world') / faceArea)
dupObj = pm.duplicate(dup)
pm.move(faceC[0], faceC[1], faceC[2], dupObj)
RotateDupObj(f.getNormal(space="world").normal(), dupObj)#旋转
dupObj[0].setScale((faceScale, faceScale, faceScale))
#沿着边进行复制
for e in obj[0].edges:
if (e.numConnectedFaces() > 1):
averagePoin = pm.datatypes.Vector(0, 0, 0)
averageN = pm.datatypes.Vector(0, 0, 0)
xAixsRot = pm.datatypes.Vector(0, 0, 0)
faceScale = 0.0
for i in e.connectedFaces():
averageN = averageN + i.getNormal(space="world").normal()
averagePoin = averagePoin + faceCenter(i)
xAixsRot = faceCenter(i) - xAixsRot
faceScale = faceScale + i.getArea(space='world')
dupObj = pm.duplicate(dup)
averageN = averageN / 2
faceScale = math.sqrt((faceScale / 2) / faceArea)
averagePoin = averagePoin / 2
xAixsRot.normalize()
cross = averageN.cross(xAixsRot).normal()
dupMaix = dupObj[0].getMatrix(worldSpace=True)
#重构旋转矩阵
dupObj[0].setMatrix((xAixsRot.x * faceScale, xAixsRot.y * faceScale, xAixsRot.z * faceScale, 0,
averageN.x * faceScale, averageN.y * faceScale, averageN.z * faceScale, 0,
cross.x * faceScale, cross.y * faceScale, cross.z * faceScale, 0,
averagePoin.x, averagePoin.y, averagePoin.z, 1), worldSpace=True)
pm.rotate(dupObj, 90, x=True, objectSpace=True, relative=True)