在Maya中按照Edge Loop的方向对vertex ID进行排序
1. 问题描述
在Maya中想要根据空间中几个点的位置生成一个多边形网格,可以使用 cmds.polyCreateFacet(p=point_pos_list)
命令,其中p是一个包含点位置坐标的列表。这个命令会根据列表中的元素的先后顺序来绘制多边形网格。
但是在实际的使用中,当我在视图中以选择循环边的方式获取点,返回的结果是乱序的。
例如在这张图中,正确的点序应该是由一个起始点开始,单向的经过每一个点,并把它存入列表。例如如果以Vertex 0 作为起始点,沿着顺时针方向经过每一个点,那么 point_index_list = [0, 2, 1, 7, 3, 4, 5, 6]
, 而实际经过选择返回的列表是 point_index_list = [1, 2, 3, 5, 6, 7, 9]
, 可以发现,返回结果根据 Vertex ID的数值大小进行了排序。
那么这种错误的点序就会在使用polyCreate
命令时出现下图这样的结果。
2. 解决问题的思路
因此我们希望沿着选择的循环边的走向来把点的位置坐标按照顺序存入列表当中。
第一种解决方法:
首先考虑Maya中有没有现成的命令可以解决这个问题。
Maya的cmds.ls
命令中有一个Flag叫orderedSelection
。
这个Flag可以记录视图操作中选择的先后顺序,下面是官方对于标记的完整表述。
List objects and components that are currently selected in their order of selection. This flag depends on the value of the -tso/trackSelectionOrder flag of the selectPref command. If that flag is not enabled than this flag will return the same thing as the -sl/selection flag would.
使用方式:
import maya.cmds as mc
# 先要使用selectPref命令,打开trackSelectionOrder的Flag,不然就无法正确记录选择顺序
mc.selectPref(tso=True)
# 然后在视图中按照顺序手动选择点
# 最后使用ls获取选择的点集
point_index_list = mc.ls(orderedSelection=True, fl=True)
print point_index_list
这时候打印点的列表就可以发现列表是按照你选择的顺序来存储点的,先选择的点存储在列表的头部,后选择的点存储在列表的尾部。
缺点:
这种方法的缺点显而易见,每次需要每次都手动选择点的顺序,非常的繁琐;而且也不符合使用脚本实现自动化的初衷。
第二种解决方法:
既然Maya中已有的命令不能很好的处理这个问题,那么考虑自己编写一个函数来解决。
我先将问题进行拆分:
- 获取循环边上的所有点的列表
- 获取每个点的相邻点集,把不在循环边上的点给筛除掉
- 相邻点集中还剩下头和尾两个点,也就是当前点的前一个点和后一个点,我们还需要一个排序后的列表记录当前点
- 如果相邻点集中的点不在排序后的列表中,则说明是当前点的后一个点
- 把下一个点作为当前点,并循环2-4的操作
- 当排序后的列表的长度等于循环边上的所有点的列表,则说明所有点均已经排序,循环终止
实现方法
def get_pt_inOrder(edgeCache, startpoint = None):
'''
:param edgeCache: 输入循环边
:param startpoint: 输入起始点
:return: 返回排序完成的点集
'''
vertex_all = mc.polyListComponentConversion(edgeCache, fe=True, tv=True)
mc.select(vertex_all, replace=True)
vertex_all = mc.ls(sl=True, fl=True)
all_index_list = []
ordered_index_list = []
edge_obj = om.MObject()
dagPath = om.MDagPath()
selection_list = om.MSelectionList()
om.MGlobal.getActiveSelectionList(selection_list)
selIt = om.MItSelectionList(selection_list)
selIt.getDagPath(dagPath, edge_obj)
edge_iter = om.MItMeshVertex(dagPath, edge_obj)
mc.select(clear=True)
while not edge_iter.isDone():
all_index_list.append(edge_iter.index())
edge_iter.next()
# 如果有起始点则从起始点开始
# 没有则从列表的第一个元素开始
if not startpoint:
curEdge = vertex_all[0]
else:
curEdge = startpoint
while len(ordered_index_list) < len(all_index_list):
mc.select(curEdge, replace=True)
cur_edge_obj = om.MObject()
cur_dagPath = om.MDagPath()
cur_selection_list = om.MSelectionList()
om.MGlobal.getActiveSelectionList(cur_selection_list)
cur_selIt = om.MItSelectionList(cur_selection_list)
cur_selIt.getDagPath(cur_dagPath, cur_edge_obj)
cur_edge = om.MItMeshVertex(cur_dagPath, cur_edge_obj)
# print cur_edge.count()
while not cur_edge.isDone():
edgeIndex = om.MIntArray()
cur_edge.getConnectedVertices(edgeIndex)
if not ordered_index_list:
ordered_index_list.append(int(get_vaild_component(0, curEdge)[1]))
for index in edgeIndex:
# print('curEdge is {}'.format(curEdge))
# print('index is {}'.format(index))
if index in all_index_list and index not in ordered_index_list:
ordered_index_list.append(index)
# print ordered_index_list
curEdge = get_vaild_component(index, curEdge)[0]
break
cur_edge.next()
mc.select(clear=True)
final_pt = []
for index in ordered_index_list:
new_pt = get_vaild_component(index, curEdge)[0]
final_pt.append(new_pt)
# print final_pt
return final_pt
这个函数可以根据选择的循环边返回经过正确排序的点的列表。其中起始点如果不指定,默认会将列表的第一个元素作为起始点。
这里主要用了Maya API中的方法来获取每个点的相邻的点。
由于Maya API中的index()
方法返回的是点序,类型是int,所以需要转换成Maya 命令行可以认识的字符串类型。因此我又把这个功能单独使用一个函数实现。用到了Python re
模块进行正则表达式的匹配
def get_vaild_component(index, singleStr):
'''
改变component中的编号
:param index: 要改变的编号
:param singleStr: 原始的序列
:return:
'''
dotIndex = singleStr.rfind(".")
prefix = singleStr[:dotIndex + 1]
str2Change = singleStr[dotIndex + 1:]
result = re.match(u'(\w+)\[(\d+)\]', str2Change)
polyType = result.group(1)
curIndex = result.group(2)
rebuildStr = prefix + polyType + "[{0}]".format(index)
return rebuildStr, curIndex
在使用时先将这个函数写入内存中,如果直接调用get_pt_inOrder
会报错。
最后:
目前第二种解决方法可以满足需求,但是并不高效。如果有更好的方法请留言告诉我。:)