如何在多个maya网格中查找匹配的顶点

时间:2015-04-17 21:28:32

标签: python api 3d maya vertices

我正在尝试比较一个网格上的顶点与另一个网格的顶点位置,并生成一对配对顶点的列表(最终目的是将颈部地理位置上的顶点与身体地理位置的顶部顶点配对。)< / p>

我'配对'它们的方法是只比较两个网格中所有顶点之间的距离,然后通过在单独的列表中对它们进行排序来匹配彼此最接近的那些,(neck_geo_verts [0]与body_geo_verts配对[ 0])

我想使用OpenMaya,因为我听说它比cmds.xform快得多。

这是我的代码到目前为止得到的顶点,虽然它使用的是cmd而不是Maya API。我很难从Maya文档中找到我需要的东西。

# The user selects an edge on both the bottom of the neck and top of the body, then this code gets all the vertices in an edge border on both of those geos and populates two lists with the vertices

import maya.cmds as mc
import maya.api.OpenMaya as om
import re

mc.unloadPlugin('testingPlugin.py')
mc.loadPlugin('testingPlugin.py')

def main():
    geoOneVerts = []
    geoTwoVerts = []

    edges = cmds.ls(selection=True, sn=True)

    geoOneEdgeNum = re.search(r"\[([0-9_]+)\]", edges[0])
    geoTwoEdgeNum = re.search(r"\[([0-9_]+)\]", edges[1])

    cmds.polySelect(add=True, edgeBorder=int(geoOneEdgeNum.group(1)))
    geoOneEdgeBorder = cmds.ls(selection=True, sn=True)
    geoOneEdgeVerts = cmds.polyInfo(edgeToVertex=True)

    for vertex in geoOneEdgeVerts:
        vertexPairNums = re.search(r":\s*([0-9_]+)\s*([0-9_]+)", vertex)
        geoOneVerts.append(vertexPairNums.group(1))
        geoOneVerts.append(vertexPairNums.group(2))

    cmds.polySelect(replace=True, edgeBorder=int(geoTwoEdgeNum.group(1)))
    geoTwoEdgeBorder = cmds.ls(selection=True, sn=True)
    geoTwoEdgeVerts = cmds.polyInfo(edgeToVertex=True)

    for vertex in geoTwoEdgeVerts:
        vertexPairNums = re.search(r":\s*([0-9_]+)\s*([0-9_]+)", vertex)
        geoTwoVerts.append(vertexPairNums.group(1))
        geoTwoVerts.append(vertexPairNums.group(2))

    geoOneVerts = list(set(geoOneVerts))
    geoTwoVerts = list(set(geoTwoVerts))

    # How do I use OpenMaya to compare the distance from the verts in both lists?

main()

编辑:这段代码给了我两个列表,上面填充了两个网格上顶点的DAG名称。我不确定如何获取这些顶点的位置来比较两个列表中顶点之间的距离,我也不确定我是否应该使用maya.cmds而不是maya.api.OpenMaya考虑到我将要操作的顶点数量。

EDIT2:感谢Theodox和数百次搜索帮助。我最终创建了一个使用边界顶点的版本,并假设两个网格上的成对顶点都在相同的全局空间中。出于性能原因,我选择使用Maya API并完全使用Maya命令。

Vesion1(使用边界背心):

import maya.OpenMaya as om

def main():
    geo1Verts = om.MFloatPointArray()
    geo2Verts = om.MFloatPointArray()

    selectionList = om.MSelectionList()
    om.MGlobal.getActiveSelectionList(selectionList)

    geo1SeamVerts = getSeamVertsOn(selectionList, 1)
    geo2SeamVerts = getSeamVertsOn(selectionList, 2)

    pairedVertsDict = pairSeamVerts(geo1SeamVerts, geo2SeamVerts)

def getSeamVertsOn(objectList, objectNumber):
    count = 0 
    indexPointDict = {}
    selectedObject = om.MObject()

    iter = om.MItSelectionList(objectList, om.MFn.kGeometric)
    while not iter.isDone():
        count += 1

        connectedVerts = om.MIntArray()

        if (count != objectNumber):
            iter.next()
        else:
            iter.getDependNode(selectedObject)
            vertexIter = om.MItMeshVertex(selectedObject)

            while not vertexIter.isDone():
                if (vertexIter.onBoundary()):
                    vertex = om.MPoint()
                    vertex = vertexIter.position()
                    indexPointDict[int(vertexIter.index())] = vertex

                vertexIter.next()

            return indexPointDict

def pairSeamVerts (dictSeamVerts1, dictSeamVerts2):
    pairedVerts = {}

    if (len(dictSeamVerts1) >= len(dictSeamVerts2)):
        for vert1 in dictSeamVerts1:
            distance = 0
            closestDistance = 1000000
            vertPair = 0

            for vert2 in dictSeamVerts2:
                distance = dictSeamVerts1[vert1].distanceTo(dictSeamVerts2[vert2])

                if (distance < closestDistance):
                    closestDistance = distance
                    vertPair = vert2

            pairedVerts[vert1] = vertPair

        return (pairedVerts)

    else:
        for vert1 in dictSeamVerts2:
            distance = 0
            closestDistance = 1000000
            vertPair = 0

            for vert2 in dictSeamVerts1:
                distance = dictSeamVerts2[vert1].distanceTo(dictSeamVerts1[vert2])

                if (distance < closestDistance):
                    closestDistance = distance
                    vertPair = vert2

            pairedVerts[vert1] = vertPair

        return (pairedVerts)

main()

版本2(假设成对顶点将共享全局空间):

import maya.OpenMaya as om

def main():   
    selectionList = om.MSelectionList()
    om.MGlobal.getActiveSelectionList(selectionList)

    meshOneVerts = getVertPositions(selectionList, 1)
    meshTwoVerts = getVertPositions(selectionList, 2)

    meshOneHashedPoints = hashPoints(meshOneVerts)
    meshTwoHashedPoints = hashPoints(meshTwoVerts)

    matchingVertList = set(meshOneHashedPoints).intersection(meshTwoHashedPoints)

    pairedVertList = getPairIndices(meshOneHashedPoints, meshTwoHashedPoints, matchingVertList)

def getVertPositions(objectList, objectNumber):
    count = 0
    pointList = []

    iter = om.MItSelectionList(objectList, om.MFn.kGeometric)
    while not iter.isDone():
        count = count + 1
        if (count != objectNumber):
            iter.next()

        dagPath = om.MDagPath()
        iter.getDagPath(dagPath)
        mesh = om.MFnMesh(dagPath)

        meshPoints = om.MPointArray()
        mesh.getPoints(meshPoints, om.MSpace.kWorld)

        for point in range(meshPoints.length()):
            pointList.append([meshPoints[point][0], meshPoints[point][1], meshPoints[point][2]])
        return pointList

def hashPoints(pointList):
    _clamp = lambda p: hash(int(p * 10000) / 10000.00)

    hashedPointList = []

    for point in pointList:
        hashedPointList.append(hash(tuple(map(_clamp, point))))

    return (hashedPointList)

def getPairIndices(hashListOne, hashListTwo, matchingHashList):
    pairedVertIndices = []
    vertOneIndexList = []
    vertTwoIndexList = []

    for hash in matchingHashList:
        vertListOne = []
        vertListTwo = []

        for hashOne in range(len(hashListOne)):
            if (hashListOne[hashOne] == hash):
                vertListOne.append(hashOne)

        for hashTwo in range(len(hashListTwo)):
            if (hashListTwo[hashTwo] == hash):
                vertListTwo.append(hashTwo)

        pairedVertIndices.append([vertListOne, vertListTwo])

    return pairedVertIndices

main()

1 个答案:

答案 0 :(得分:2)

对于距离比较方法,API明显更快,但在这种情况下,我认为真正的杀手很可能是算法。将每个顶点与其他顶点进行比较是很多数学。

可能最简单的方法是想出一种方法来对顶点进行散列:将每个xyz点转换为单个值,可以在不进行距离的情况下与其他值进行比较:具有相同散列的两个顶点必然是在同一个位置。您可以调整哈希算法以稍微量化vert位置以同时解决浮点错误。

这是一种散列点的方法(最多4个有效数字,您可以通过更改_clamp中的常量来调整):

def point_hash(point):
    '''
    hash a tuple, probably a cmds vertex pos
    '''
    _clamp = lambda p: hash(int(p * 10000) / 10000.00)
    return hash(tuple(map(_clamp, point)))

只要两组顶点都在同一空间(可能是世界空间)中进行散列,相同的散列就意味着匹配的顶点。您所要做的就是遍历每个网格,创建一个字典,将顶点哈希键入顶点索引。这是在cmds中执行此操作的方法:

def vert_dict(obj):
    '''
    returns a dictionary of hash: index pairs representing the hashed verts of <obj>
    '''
    results = dict()
    verts = cmds.xform(obj + ".vtx[*]", q=True, t=True, ws=True)
    total = len(verts)/ 3
    for v in range(total):
        idx = v * 3
        hsh = point_hash (verts[idx: idx + 3])
        results[hsh] = v
    return results         

您可以通过交叉来自两个词典的键来找到相交的顶点 - 两个网格中存在的顶点。然后通过在两个词典中查找,将两个网格中的匹配顶点转换回顶点索引。

除非网格非常重,否则在没有API的情况下这应该是可行的,因为所有工作都在没有API模拟的哈希函数中。

唯一可能的问题是确保顶点位于同一空间。如果由于某种原因无法将顶点放入同一空间,则必须依靠基于距离的策略。